package multiplex import ( "bufio" "bytes" "github.com/cbeuw/Cloak/internal/util" "io/ioutil" "math/rand" "net" "testing" "time" ) func setupSesh(unordered bool) *Session { sessionKey := make([]byte, 32) rand.Read(sessionKey) obfuscator, _ := GenerateObfs(0x00, sessionKey) seshConfig := &SessionConfig{ Obfuscator: obfuscator, Valve: nil, UnitRead: util.ReadTLS, Unordered: unordered, } return MakeSession(0, seshConfig) } type blackhole struct { hole *bufio.Writer } func newBlackHole() *blackhole { return &blackhole{hole: bufio.NewWriter(ioutil.Discard)} } func (b *blackhole) Read([]byte) (int, error) { time.Sleep(1 * time.Hour) return 0, nil } func (b *blackhole) Write(in []byte) (int, error) { return b.hole.Write(in) } func (b *blackhole) Close() error { return nil } func (b *blackhole) LocalAddr() net.Addr { ret, _ := net.ResolveTCPAddr("tcp", "127.0.0.1") return ret } func (b *blackhole) RemoteAddr() net.Addr { ret, _ := net.ResolveTCPAddr("tcp", "127.0.0.1") return ret } func (b *blackhole) SetDeadline(t time.Time) error { return nil } func (b *blackhole) SetReadDeadline(t time.Time) error { return nil } func (b *blackhole) SetWriteDeadline(t time.Time) error { return nil } func BenchmarkStream_Write_Ordered(b *testing.B) { const PAYLOAD_LEN = 1000 hole := newBlackHole() sesh := setupSesh(false) sesh.AddConnection(hole) testData := make([]byte, PAYLOAD_LEN) rand.Read(testData) stream, _ := sesh.OpenStream() b.ResetTimer() for i := 0; i < b.N; i++ { _, err := stream.Write(testData) if err != nil { b.Error( "For", "stream write", "got", err, ) } b.SetBytes(PAYLOAD_LEN) } } func BenchmarkStream_Read_Ordered(b *testing.B) { sesh := setupSesh(false) const PAYLOAD_LEN = 1000 testPayload := make([]byte, PAYLOAD_LEN) rand.Read(testPayload) f := &Frame{ 1, 0, 0, testPayload, } obfsBuf := make([]byte, 17000) l, _ := net.Listen("tcp", "127.0.0.1:0") go func() { // potentially bottlenecked here rather than the actual stream read throughput conn, _ := net.Dial("tcp", l.Addr().String()) for { i, _ := sesh.Obfs(f, obfsBuf) f.Seq += 1 _, err := conn.Write(obfsBuf[:i]) if err != nil { b.Error("cannot write to connection", err) } } }() conn, _ := l.Accept() sesh.AddConnection(conn) stream, err := sesh.Accept() if err != nil { b.Error("failed to accept stream", err) } //time.Sleep(5*time.Second) // wait for buffer to fill up readBuf := make([]byte, PAYLOAD_LEN) b.ResetTimer() for j := 0; j < b.N; j++ { n, err := stream.Read(readBuf) if !bytes.Equal(readBuf, testPayload) { b.Error("paylod not equal") } b.SetBytes(int64(n)) if err != nil { b.Error(err) } } } func TestStream_Read(t *testing.T) { sesh := setupSesh(false) testPayload := []byte{42, 42, 42} const PAYLOAD_LEN = 3 f := &Frame{ 1, 0, 0, testPayload, } ch := make(chan []byte) l, _ := net.Listen("tcp", "127.0.0.1:0") go func() { conn, _ := net.Dial("tcp", l.Addr().String()) for { data := <-ch _, err := conn.Write(data) if err != nil { t.Error("cannot write to connection", err) } } }() conn, _ := l.Accept() sesh.AddConnection(conn) var streamID uint32 buf := make([]byte, 10) obfsBuf := make([]byte, 512) t.Run("Plain read", func(t *testing.T) { f.StreamID = streamID i, _ := sesh.Obfs(f, obfsBuf) streamID++ ch <- obfsBuf[:i] time.Sleep(100 * time.Microsecond) stream, err := sesh.Accept() if err != nil { t.Error("failed to accept stream", err) } i, err = stream.Read(buf) if err != nil { t.Error("failed to read", err) } if i != PAYLOAD_LEN { t.Errorf("expected read %v, got %v", PAYLOAD_LEN, i) } if !bytes.Equal(buf[:i], testPayload) { t.Error("expected", testPayload, "got", buf[:i]) } }) t.Run("Nil buf", func(t *testing.T) { f.StreamID = streamID i, _ := sesh.Obfs(f, obfsBuf) streamID++ ch <- obfsBuf[:i] time.Sleep(100 * time.Microsecond) stream, _ := sesh.Accept() i, err := stream.Read(nil) if i != 0 || err != nil { t.Error("expecting", 0, nil, "got", i, err) } stream.Close() i, err = stream.Read(nil) if i != 0 || err != ErrBrokenStream { t.Error("expecting", 0, ErrBrokenStream, "got", i, err) } }) t.Run("Read after stream close", func(t *testing.T) { f.StreamID = streamID i, _ := sesh.Obfs(f, obfsBuf) streamID++ ch <- obfsBuf[:i] time.Sleep(100 * time.Microsecond) stream, _ := sesh.Accept() stream.Close() i, err := stream.Read(buf) if err != nil { t.Error("failed to read", err) } if i != PAYLOAD_LEN { t.Errorf("expected read %v, got %v", PAYLOAD_LEN, 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) streamID++ ch <- obfsBuf[:i] time.Sleep(100 * time.Microsecond) stream, _ := sesh.Accept() sesh.Close() i, err := stream.Read(buf) if err != nil { t.Error("failed to read", err) } if i != PAYLOAD_LEN { t.Errorf("expected read %v, got %v", PAYLOAD_LEN, 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") } }) } func TestStream_UnorderedRead(t *testing.T) { sesh := setupSesh(true) testPayload := []byte{42, 42, 42} const PAYLOAD_LEN = 3 f := &Frame{ 1, 0, 0, testPayload, } ch := make(chan []byte) l, _ := net.Listen("tcp", "127.0.0.1:0") go func() { conn, _ := net.Dial("tcp", l.Addr().String()) for { data := <-ch _, err := conn.Write(data) if err != nil { t.Error("cannot write to connection", err) } } }() conn, _ := l.Accept() sesh.AddConnection(conn) var streamID uint32 buf := make([]byte, 10) obfsBuf := make([]byte, 512) t.Run("Plain read", func(t *testing.T) { f.StreamID = streamID i, _ := sesh.Obfs(f, obfsBuf) streamID++ ch <- obfsBuf[:i] time.Sleep(100 * time.Microsecond) stream, err := sesh.Accept() if err != nil { t.Error("failed to accept stream", err) } i, err = stream.Read(buf) if err != nil { t.Error("failed to read", err) } if i != PAYLOAD_LEN { t.Errorf("expected read %v, got %v", PAYLOAD_LEN, i) } if !bytes.Equal(buf[:i], testPayload) { t.Error("expected", testPayload, "got", buf[:i]) } }) t.Run("Nil buf", func(t *testing.T) { f.StreamID = streamID i, _ := sesh.Obfs(f, obfsBuf) streamID++ ch <- obfsBuf[:i] time.Sleep(100 * time.Microsecond) stream, _ := sesh.Accept() i, err := stream.Read(nil) if i != 0 || err != nil { t.Error("expecting", 0, nil, "got", i, err) } stream.Close() i, err = stream.Read(nil) if i != 0 || err != ErrBrokenStream { t.Error("expecting", 0, ErrBrokenStream, "got", i, err) } }) t.Run("Read after stream close", func(t *testing.T) { f.StreamID = streamID i, _ := sesh.Obfs(f, obfsBuf) streamID++ ch <- obfsBuf[:i] time.Sleep(100 * time.Microsecond) stream, _ := sesh.Accept() stream.Close() i, err := stream.Read(buf) if err != nil { t.Error("failed to read", err) } if i != PAYLOAD_LEN { t.Errorf("expected read %v, got %v", PAYLOAD_LEN, 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) streamID++ ch <- obfsBuf[:i] time.Sleep(100 * time.Microsecond) stream, _ := sesh.Accept() sesh.Close() i, err := stream.Read(buf) if err != nil { t.Error("failed to read", err) } if i != PAYLOAD_LEN { t.Errorf("expected read %v, got %v", PAYLOAD_LEN, 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") } }) }