Skip to content

Commit

Permalink
Implement E2E test for Client/Server
Browse files Browse the repository at this point in the history
Relates to #11
  • Loading branch information
Sean-Der committed Dec 11, 2018
1 parent 092fceb commit 0c9bb3e
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 3 deletions.
7 changes: 5 additions & 2 deletions pkg/dtls/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func (c *Conn) Write(p []byte) (int, error) {
// Close closes the connection.
func (c *Conn) Close() error {
c.notify(alertLevelFatal, alertCloseNotify)
c.stopWithError(errConnClosed)
c.stopWithError(ErrConnClosed)
return c.nextConn.Close()
}

Expand Down Expand Up @@ -347,8 +347,9 @@ func (c *Conn) notify(level alertLevel, desc alertDescription) {

func (c *Conn) signalHandshakeComplete() {
select {
case c.handshakeCompleted <- true:
case <-c.handshakeCompleted:
default:
close(c.handshakeCompleted)
}
}

Expand All @@ -360,6 +361,8 @@ func (c *Conn) startHandshakeOutbound() {
err error
)
select {
case <-c.handshakeCompleted:
return
case <-c.workerTicker.C:
isFinished, err = c.flightHandler(c)
case <-c.currFlight.workerTrigger:
Expand Down
4 changes: 3 additions & 1 deletion pkg/dtls/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package dtls

import "errors"

// Typed errors
var (
errConnClosed = errors.New("dtls: conn is closed")
ErrConnClosed = errors.New("dtls: conn is closed")

errBufferTooSmall = errors.New("dtls: buffer is too small")
errCertificateUnset = errors.New("dtls: handshakeMessageCertificate can not be marshalled without a certificate")
errCipherSuiteNoIntersection = errors.New("dtls: Client+Server do not support any shared cipher suites")
Expand Down
136 changes: 136 additions & 0 deletions test/e2e/pion/simple_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package pion

import (
"fmt"
"net"
"runtime"
"sync/atomic"
"testing"
"time"

"github.com/pions/dtls/pkg/dtls"
)

const testMessage = "Hello World"
const testTimeLimit = 5 * time.Second
const messageRetry = 200 * time.Millisecond
const expectedGoRoutineCount = 3

// Counter to make sure both sides got a message
var messageRecvCount uint64

func simpleReadWrite(errChan chan error, outChan chan string, conn net.Conn) {
go func() {
buffer := make([]byte, 8192)
for {
n, err := conn.Read(buffer)
if err != nil {
errChan <- err
return
}

outChan <- string(buffer[:n])
atomic.AddUint64(&messageRecvCount, 1)
return
}
}()

for {
if atomic.LoadUint64(&messageRecvCount) == 2 {
break
} else if _, err := conn.Write([]byte(testMessage)); err != nil {
errChan <- err
break
}

time.Sleep(messageRetry)
}

conn.Close()
}

/*
Simple DTLS Client/Server can communicate
- Assert that you can send messages both ways
- Assert that Close() on both ends work
- Assert that no Goroutines are leaked
*/
func TestPionE2ESimple(t *testing.T) {
errChan := make(chan error)
clientChan := make(chan string)
serverChan := make(chan string)

cert, key, err := createSelfSigned()
if err != nil {
t.Fatal(err)
}

// DTLS Client
go func() {
conn, err := dtls.Dial("udp",
&net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: e2eServerPort},
&dtls.Config{Certificate: cert, PrivateKey: key},
)
if err != nil {
errChan <- err
return
}
simpleReadWrite(errChan, clientChan, conn)
}()

// DTLS Server
go func() {
listener, err := dtls.Listen("udp",
&net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: e2eServerPort},
&dtls.Config{Certificate: cert, PrivateKey: key},
)
if err != nil {
errChan <- err
return
}
conn, err := listener.Accept()
if err != nil {
errChan <- err
return
}

simpleReadWrite(errChan, serverChan, conn)
}()

func() {
seenClient, seenServer := false, false
for {
select {
case err := <-errChan:
fmt.Println(err)
case <-time.After(testTimeLimit):
t.Fatalf("Test timeout, seenClient %t seenServer %t", seenClient, seenServer)
case clientMsg := <-clientChan:
if clientMsg != testMessage {
t.Fatalf("clientMsg does not equal test message: %s %s", clientMsg, testMessage)
}

seenClient = true
if seenClient && seenServer {
return
}
case serverMsg := <-serverChan:
if serverMsg != testMessage {
t.Fatalf("serverMsg does not equal test message: %s %s", serverMsg, testMessage)
}

seenServer = true
if seenClient && seenServer {
return
}
}
}
}()

// We have seen Client+Server communication! Now count goroutines to make sure we haven't leaked
time.Sleep(time.Second) // TODO this could be racey
goRoutineCount := runtime.NumGoroutine()
if goRoutineCount != expectedGoRoutineCount {
t.Fatalf("goRoutineCount != expectedGoRoutineCount, possible leak: %d %d", goRoutineCount, expectedGoRoutineCount)
}
}
61 changes: 61 additions & 0 deletions test/e2e/pion/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package pion

import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"math/big"
"time"
)

const e2eServerPort = 5555
const e2eBufferSize = 8192

func createSelfSigned() (*x509.Certificate, crypto.PrivateKey, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, err
}

origin := make([]byte, 16)

// Max random value, a 130-bits integer, i.e 2^130 - 1
maxBigInt := new(big.Int)
/* #nosec */
maxBigInt.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxBigInt, big.NewInt(1))
serialNumber, err := rand.Int(rand.Reader, maxBigInt)
if err != nil {
return nil, nil, err
}

template := x509.Certificate{
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
x509.ExtKeyUsageServerAuth,
},
BasicConstraintsValid: true,
NotBefore: time.Now(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
NotAfter: time.Now().AddDate(0, 1, 0),
SerialNumber: serialNumber,
Version: 2,
Subject: pkix.Name{CommonName: hex.EncodeToString(origin)},
IsCA: true,
}

raw, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return nil, nil, err
}

cert, err := x509.ParseCertificate(raw)
if err != nil {
return nil, nil, err
}

return cert, priv, nil
}

0 comments on commit 0c9bb3e

Please sign in to comment.