Repository: pion/dtls
Pull request #217 (2020-03-09 04:56:50)

Move replaydetector to pion/transport
To share replaydetector with pion/srtp package.

### Reference issue
Part of #216 
https://github.com/pion/transport/pull/59

codecov[bot]: # [Codecov](https://codecov.io/gh/pion/dtls/pull/217?src=pr&el=h1) Report
> Merging [#217](https://codecov.io/gh/pion/dtls/pull/217?src=pr&el=desc) into [master](https://codecov.io/gh/pion/dtls/commit/1a6006035b6484338e08224ebfdc5ed1cad1b5e3?src=pr&el=desc) will **decrease** coverage by `0.15%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/pion/dtls/pull/217/graphs/tree.svg?width=650&token=bcCB2jB88I&height=150&src=pr)](https://codecov.io/gh/pion/dtls/pull/217?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #217      +/-   ##
==========================================
- Coverage   77.09%   76.93%   -0.16%     
==========================================
  Files          75       73       -2     
  Lines        4055     3997      -58     
==========================================
- Hits         3126     3075      -51     
+ Misses        627      624       -3     
+ Partials      302      298       -4
```

| Flag | Coverage Δ | |
|---|---|---|
| #go | `76.88% <ø> (-0.19%)` | :arrow_down: |
| #wasm | `71.47% <ø> (-0.31%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pion/dtls/pull/217?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [state.go](https://codecov.io/gh/pion/dtls/pull/217/diff?src=pr&el=tree#diff-c3RhdGUuZ28=) | `68.29% <ø> (ø)` | :arrow_up: |
| [conn.go](https://codecov.io/gh/pion/dtls/pull/217/diff?src=pr&el=tree#diff-Y29ubi5nbw==) | `83.62% <ø> (ø)` | :arrow_up: |
| [internal/net/udp/conn.go](https://codecov.io/gh/pion/dtls/pull/217/diff?src=pr&el=tree#diff-aW50ZXJuYWwvbmV0L3VkcC9jb25uLmdv) | `86.71% <0%> (+1.56%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pion/dtls/pull/217?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pion/dtls/pull/217?src=pr&el=footer). Last update [1a60060...59e12b8](https://codecov.io/gh/pion/dtls/pull/217?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/conn.go b/conn.go
index c518c72..a8c515e 100644
--- a/conn.go
+++ b/conn.go
@@ -12,8 +12,8 @@ import (
 	"github.com/pion/dtls/v2/internal/closer"
 	"github.com/pion/dtls/v2/internal/net/connctx"
 	"github.com/pion/dtls/v2/internal/net/deadline"
-	"github.com/pion/dtls/v2/internal/replaydetector"
 	"github.com/pion/logging"
+	"github.com/pion/transport/replaydetector"
 )
 
 const (
@@ -259,20 +259,20 @@ func ServerWithContext(ctx context.Context, conn net.Conn, config *Config) (*Con
 
 // Read reads data from the connection.
 func (c *Conn) Read(p []byte) (n int, err error) {
+	if !c.isHandshakeCompletedSuccessfully() {
+		return 0, errHandshakeInProgress
+	}
+
 	select {
 	case <-c.readDeadline.Done():
-		return 0, context.DeadlineExceeded
+		return 0, errDeadlineExceeded
 	default:
 	}
 
-	if !c.isHandshakeCompletedSuccessfully() {
-		return 0, errHandshakeInProgress
-	}
-
 	for {
 		select {
 		case <-c.readDeadline.Done():
-			return 0, context.DeadlineExceeded
+			return 0, errDeadlineExceeded
 		case <-c.closed.Done():
 			return 0, io.EOF
 		case out, ok := <-c.decrypted:
@@ -295,18 +295,19 @@ func (c *Conn) Read(p []byte) (n int, err error) {
 
 // Write writes len(p) bytes from p to the DTLS connection
 func (c *Conn) Write(p []byte) (int, error) {
+	if c.isConnectionClosed() {
+		return 0, ErrConnClosed
+	}
+
 	select {
 	case <-c.writeDeadline.Done():
-		return 0, context.DeadlineExceeded
+		return 0, errDeadlineExceeded
 	default:
 	}
 
 	if !c.isHandshakeCompletedSuccessfully() {
 		return 0, errHandshakeInProgress
 	}
-	if c.isConnectionClosed() {
-		return 0, ErrConnClosed
-	}
 
 	return len(p), c.writePackets(c.writeDeadline, []*packet{
 		{
@@ -421,7 +422,7 @@ func (c *Conn) writePackets(ctx context.Context, pkts []*packet) error {
 
 	for _, compactedRawPackets := range compactedRawPackets {
 		if _, err := c.nextConn.Write(ctx, compactedRawPackets); err != nil {
-			return err
+			return netError(err)
 		}
 	}
 
@@ -569,7 +570,7 @@ func (c *Conn) readAndBuffer(ctx context.Context) error {
 	b := *bufptr
 	i, err := c.nextConn.Read(ctx, b)
 	if err != nil {
-		return err
+		return netError(err)
 	}
 
 	pkts, err := unpackDatagram(b[:i])
diff --git a/errors.go b/errors.go
index aaa281a..aa007bd 100644
--- a/errors.go
+++ b/errors.go
@@ -4,75 +4,145 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"io"
+	"net"
+	"os"
 
 	"golang.org/x/xerrors"
 )
 
 // Typed errors
 var (
-	ErrConnClosed = errors.New("dtls: conn is closed")
-
-	errBufferTooSmall                    = errors.New("dtls: buffer is too small")
-	errClientCertificateRequired         = errors.New("dtls: server required client verification, but got none")
-	errClientCertificateNotVerified      = errors.New("dtls: client sent certificate but did not verify it")
-	errCertificateVerifyNoCertificate    = errors.New("dtls: client sent certificate verify but we have no certificate to verify")
-	errNoCertificates                    = errors.New("dtls: no certificates configured")
-	errCipherSuiteNoIntersection         = errors.New("dtls: Client+Server do not support any shared cipher suites")
-	errCipherSuiteUnset                  = errors.New("dtls: server hello can not be created without a cipher suite")
-	errCompressionMethodUnset            = errors.New("dtls: server hello can not be created without a compression method")
-	errContextUnsupported                = errors.New("dtls: context is not supported for ExportKeyingMaterial")
-	errCookieMismatch                    = errors.New("dtls: Client+Server cookie does not match")
-	errCookieTooLong                     = errors.New("dtls: cookie must not be longer then 255 bytes")
-	errDTLSPacketInvalidLength           = errors.New("dtls: packet is too short")
-	errHandshakeInProgress               = errors.New("dtls: Handshake is in progress")
-	errHandshakeMessageUnset             = errors.New("dtls: handshake message unset, unable to marshal")
-	errInvalidCipherSpec                 = errors.New("dtls: cipher spec invalid")
-	errInvalidCipherSuite                = errors.New("dtls: invalid or unknown cipher suite")
-	errInvalidCompressionMethod          = errors.New("dtls: invalid or unknown compression method")
-	errInvalidContentType                = errors.New("dtls: invalid content type")
-	errInvalidECDSASignature             = errors.New("dtls: ECDSA signature contained zero or negative values")
-	errInvalidEllipticCurveType          = errors.New("dtls: invalid or unknown elliptic curve type")
-	errInvalidExtensionType              = errors.New("dtls: invalid extension type")
-	errInvalidSNIFormat                  = errors.New("dtls: invalid server name format")
-	errInvalidHashAlgorithm              = errors.New("dtls: invalid hash algorithm")
-	errInvalidMAC                        = errors.New("dtls: invalid mac")
-	errInvalidNamedCurve                 = errors.New("dtls: invalid named curve")
-	errInvalidPrivateKey                 = errors.New("dtls: invalid private key type")
-	errInvalidSignatureAlgorithm         = errors.New("dtls: invalid signature algorithm")
-	errInvalidFlight                     = errors.New("dtls: invalid flight number")
-	errKeySignatureGenerateUnimplemented = errors.New("dtls: Unable to generate key signature, unimplemented")
-	errKeySignatureMismatch              = errors.New("dtls: Expected and actual key signature do not match")
-	errKeySignatureVerifyUnimplemented   = errors.New("dtls: Unable to verify key signature, unimplemented")
-	errLengthMismatch                    = errors.New("dtls: data length and declared length do not match")
-	errNilNextConn                       = errors.New("dtls: Conn can not be created with a nil nextConn")
-	errNotEnoughRoomForNonce             = errors.New("dtls: Buffer not long enough to contain nonce")
-	errNotImplemented                    = errors.New("dtls: feature has not been implemented yet")
-	errReservedExportKeyingMaterial      = errors.New("dtls: ExportKeyingMaterial can not be used with a reserved label")
-	errSequenceNumberOverflow            = errors.New("dtls: sequence number overflow")
-	errServerMustHaveCertificate         = errors.New("dtls: Certificate is mandatory for server")
-	errUnableToMarshalFragmented         = errors.New("dtls: unable to marshal fragmented handshakes")
-	errVerifyDataMismatch                = errors.New("dtls: Expected and actual verify data does not match")
-	errNoConfigProvided                  = errors.New("dtls: No config provided")
-	errPSKAndCertificate                 = errors.New("dtls: Certificate and PSK provided")
-	errPSKAndIdentityMustBeSetForClient  = errors.New("dtls: PSK and PSK Identity Hint must both be set for client")
-	errIdentityNoPSK                     = errors.New("dtls: Identity Hint provided but PSK is nil")
-	errNoAvailableCipherSuites           = errors.New("dtls: Connection can not be created, no CipherSuites satisfy this Config")
-	errInvalidClientKeyExchange          = errors.New("dtls: Unable to determine if ClientKeyExchange is a public key or PSK Identity")
-	errNoSupportedEllipticCurves         = errors.New("dtls: Client requested zero or more elliptic curves that are not supported by the server")
-	errRequestedButNoSRTPExtension       = errors.New("dtls: SRTP support was requested but server did not respond with use_srtp extension")
-	errClientNoMatchingSRTPProfile       = errors.New("dtls: Server responded with SRTP Profile we do not support")
-	errServerNoMatchingSRTPProfile       = errors.New("dtls: Client requested SRTP but we have no matching profiles")
-	errServerRequiredButNoClientEMS      = errors.New("dtls: Server requires the Extended Master Secret extension, but the client does not support it")
-	errClientRequiredButNoServerEMS      = errors.New("dtls: Client required Extended Master Secret extension, but server does not support it")
-	errInvalidCertificate                = errors.New("dtls: No certificate provided")
-
-	// Wrapped errors
-	errHandshakeTimeout = newNetError(
-		xerrors.Errorf("dtls: The connection timed out during the handshake: %w", context.DeadlineExceeded),
-		true, false,
-	)
+	ErrConnClosed = &ErrFatal{errors.New("conn is closed")}
+
+	errDeadlineExceeded = &ErrTimeout{xerrors.Errorf("read/write timeout: %w", context.DeadlineExceeded)}
+
+	errBufferTooSmall               = &ErrTemporary{errors.New("buffer is too small")}
+	errContextUnsupported           = &ErrTemporary{errors.New("context is not supported for ExportKeyingMaterial")}
+	errDTLSPacketInvalidLength      = &ErrTemporary{errors.New("packet is too short")}
+	errHandshakeInProgress          = &ErrTemporary{errors.New("handshake is in progress")}
+	errInvalidContentType           = &ErrTemporary{errors.New("invalid content type")}
+	errInvalidMAC                   = &ErrTemporary{errors.New("invalid mac")}
+	errInvalidPacketLength          = &ErrTemporary{errors.New("packet length and declared length do not match")}
+	errReservedExportKeyingMaterial = &ErrTemporary{errors.New("ExportKeyingMaterial can not be used with a reserved label")}
+
+	errCertificateVerifyNoCertificate   = &ErrFatal{errors.New("client sent certificate verify but we have no certificate to verify")}
+	errCipherSuiteNoIntersection        = &ErrFatal{errors.New("client+server do not support any shared cipher suites")}
+	errCipherSuiteUnset                 = &ErrFatal{errors.New("server hello can not be created without a cipher suite")}
+	errClientCertificateNotVerified     = &ErrFatal{errors.New("client sent certificate but did not verify it")}
+	errClientCertificateRequired        = &ErrFatal{errors.New("server required client verification, but got none")}
+	errClientNoMatchingSRTPProfile      = &ErrFatal{errors.New("server responded with SRTP Profile we do not support")}
+	errClientRequiredButNoServerEMS     = &ErrFatal{errors.New("client required Extended Master Secret extension, but server does not support it")}
+	errCompressionMethodUnset           = &ErrFatal{errors.New("server hello can not be created without a compression method")}
+	errCookieMismatch                   = &ErrFatal{errors.New("client+server cookie does not match")}
+	errCookieTooLong                    = &ErrFatal{errors.New("cookie must not be longer then 255 bytes")}
+	errHandshakeTimeout                 = &ErrFatal{xerrors.Errorf("the connection timed out during the handshake: %w", context.DeadlineExceeded)}
+	errIdentityNoPSK                    = &ErrFatal{errors.New("PSK Identity Hint provided but PSK is nil")}
+	errInvalidCertificate               = &ErrFatal{errors.New("no certificate provided")}
+	errInvalidCipherSpec                = &ErrFatal{errors.New("cipher spec invalid")}
+	errInvalidCipherSuite               = &ErrFatal{errors.New("invalid or unknown cipher suite")}
+	errInvalidClientKeyExchange         = &ErrFatal{errors.New("unable to determine if ClientKeyExchange is a public key or PSK Identity")}
+	errInvalidCompressionMethod         = &ErrFatal{errors.New("invalid or unknown compression method")}
+	errInvalidECDSASignature            = &ErrFatal{errors.New("ECDSA signature contained zero or negative values")}
+	errInvalidEllipticCurveType         = &ErrFatal{errors.New("invalid or unknown elliptic curve type")}
+	errInvalidExtensionType             = &ErrFatal{errors.New("invalid extension type")}
+	errInvalidHashAlgorithm             = &ErrFatal{errors.New("invalid hash algorithm")}
+	errInvalidNamedCurve                = &ErrFatal{errors.New("invalid named curve")}
+	errInvalidPrivateKey                = &ErrFatal{errors.New("invalid private key type")}
+	errInvalidSNIFormat                 = &ErrFatal{errors.New("invalid server name format")}
+	errInvalidSignatureAlgorithm        = &ErrFatal{errors.New("invalid signature algorithm")}
+	errKeySignatureMismatch             = &ErrFatal{errors.New("expected and actual key signature do not match")}
+	errNilNextConn                      = &ErrFatal{errors.New("Conn can not be created with a nil nextConn")}
+	errNoAvailableCipherSuites          = &ErrFatal{errors.New("connection can not be created, no CipherSuites satisfy this Config")}
+	errNoCertificates                   = &ErrFatal{errors.New("no certificates configured")}
+	errNoConfigProvided                 = &ErrFatal{errors.New("no config provided")}
+	errNoSupportedEllipticCurves        = &ErrFatal{errors.New("client requested zero or more elliptic curves that are not supported by the server")}
+	errPSKAndCertificate                = &ErrFatal{errors.New("Certificate and PSK provided")} // nolint:stylecheck
+	errPSKAndIdentityMustBeSetForClient = &ErrFatal{errors.New("PSK and PSK Identity Hint must both be set for client")}
+	errRequestedButNoSRTPExtension      = &ErrFatal{errors.New("SRTP support was requested but server did not respond with use_srtp extension")}
+	errServerMustHaveCertificate        = &ErrFatal{errors.New("Certificate is mandatory for server")} // nolint:stylecheck
+	errServerNoMatchingSRTPProfile      = &ErrFatal{errors.New("client requested SRTP but we have no matching profiles")}
+	errServerRequiredButNoClientEMS     = &ErrFatal{errors.New("server requires the Extended Master Secret extension, but the client does not support it")}
+	errVerifyDataMismatch               = &ErrFatal{errors.New("expected and actual verify data does not match")}
+
+	errHandshakeMessageUnset             = &ErrInternal{errors.New("handshake message unset, unable to marshal")}
+	errInvalidFlight                     = &ErrInternal{errors.New("invalid flight number")}
+	errKeySignatureGenerateUnimplemented = &ErrInternal{errors.New("unable to generate key signature, unimplemented")}
+	errKeySignatureVerifyUnimplemented   = &ErrInternal{errors.New("unable to verify key signature, unimplemented")}
+	errLengthMismatch                    = &ErrInternal{errors.New("data length and declared length do not match")}
+	errNotEnoughRoomForNonce             = &ErrInternal{errors.New("buffer not long enough to contain nonce")}
+	errNotImplemented                    = &ErrInternal{errors.New("feature has not been implemented yet")}
+	errSequenceNumberOverflow            = &ErrInternal{errors.New("sequence number overflow")}
+	errUnableToMarshalFragmented         = &ErrInternal{errors.New("unable to marshal fragmented handshakes")}
 )
 
+// ErrFatal indicates that the DTLS connection is no longer available.
+// It is mainly caused by wrong configuration of server or client.
+type ErrFatal struct {
+	Err error
+}
+
+// ErrInternal indicates and internal error caused by the implementation, and the DTLS connection is no longer available.
+// It is mainly caused by bugs or tried to use unimplemented features.
+type ErrInternal struct {
+	Err error
+}
+
+// ErrTemporary indicates that the DTLS connection is still available, but the request was failed temporary.
+type ErrTemporary struct {
+	Err error
+}
+
+// ErrTimeout indicates that the request was timed out.
+type ErrTimeout struct {
+	Err error
+}
+
+// Timeout implements net.Error.Timeout()
+func (*ErrFatal) Timeout() bool { return false }
+
+// Temporary implements net.Error.Temporary()
+func (*ErrFatal) Temporary() bool { return false }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *ErrFatal) Unwrap() error { return e.Err }
+
+func (e *ErrFatal) Error() string { return fmt.Sprintf("dtls fatal: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (*ErrInternal) Timeout() bool { return false }
+
+// Temporary implements net.Error.Temporary()
+func (*ErrInternal) Temporary() bool { return false }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *ErrInternal) Unwrap() error { return e.Err }
+
+func (e *ErrInternal) Error() string { return fmt.Sprintf("dtls internal: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (*ErrTemporary) Timeout() bool { return false }
+
+// Temporary implements net.Error.Temporary()
+func (*ErrTemporary) Temporary() bool { return true }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *ErrTemporary) Unwrap() error { return e.Err }
+
+func (e *ErrTemporary) Error() string { return fmt.Sprintf("dtls temporary: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (*ErrTimeout) Timeout() bool { return true }
+
+// Temporary implements net.Error.Temporary()
+func (*ErrTimeout) Temporary() bool { return true }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *ErrTimeout) Unwrap() error { return e.Err }
+
+func (e *ErrTimeout) Error() string { return fmt.Sprintf("dtls timeout: %v", e.Err) }
+
+// errAlert wraps DTLS alert notification as an error
 type errAlert struct {
 	*alert
 }
@@ -85,24 +155,25 @@ func (e *errAlert) IsFatalOrCloseNotify() bool {
 	return e.alertLevel == alertLevelFatal || e.alertDescription == alertCloseNotify
 }
 
-type netError struct {
-	error
-	timeout   bool
-	temporary bool
-}
-
-func newNetError(err error, timeout, temporary bool) error {
-	return &netError{
-		error:     err,
-		timeout:   timeout,
-		temporary: temporary,
+// netError translates an error from underlying Conn to corresponding net.Error.
+func netError(err error) error {
+	switch err {
+	case io.EOF, context.Canceled, context.DeadlineExceeded:
+		// Return io.EOF and context errors as is.
+		return err
 	}
-}
-
-func (e *netError) Timeout() bool {
-	return e.timeout
-}
-
-func (e *netError) Temporary() bool {
-	return e.temporary
+	switch e := err.(type) {
+	case (*net.OpError):
+		if se, ok := e.Err.(*os.SyscallError); ok {
+			if se.Timeout() {
+				return &ErrTimeout{err}
+			}
+			if isOpErrorTemporary(se) {
+				return &ErrTemporary{err}
+			}
+		}
+	case (net.Error):
+		return err
+	}
+	return &ErrFatal{err}
 }
diff --git a/errors_errno.go b/errors_errno.go
new file mode 100644
index 0000000..a9a439b
--- /dev/null
+++ b/errors_errno.go
@@ -0,0 +1,25 @@
+// +build aix darwin dragonfly freebsd linux nacl nacljs netbsd openbsd solaris windows
+
+// For systems having syscall.Errno.
+// Update build targets by following command:
+// $ grep -R ECONN $(go env GOROOT)/src/syscall/zerrors_*.go \
+//     | tr "." "_" | cut -d"_" -f"2" | sort | uniq
+
+package dtls
+
+import (
+	"os"
+	"syscall"
+)
+
+func isOpErrorTemporary(err *os.SyscallError) bool {
+	if ne, ok := err.Err.(syscall.Errno); ok {
+		switch ne {
+		case syscall.ECONNREFUSED:
+			return true
+		default:
+			return false
+		}
+	}
+	return false
+}
diff --git a/errors_noerrno.go b/errors_noerrno.go
new file mode 100644
index 0000000..fcc37ce
--- /dev/null
+++ b/errors_noerrno.go
@@ -0,0 +1,14 @@
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!nacl,!nacljs,!netbsd,!openbsd,!solaris,!windows
+
+// For systems without syscall.Errno.
+// Build targets must be inverse of errors_errno.go
+
+package dtls
+
+import (
+	"os"
+)
+
+func isOpErrorTemporary(err *os.SyscallError) bool {
+	return false
+}
diff --git a/examples/util/util.go b/examples/util/util.go
index 06cf02f..bc08688 100644
--- a/examples/util/util.go
+++ b/examples/util/util.go
@@ -5,6 +5,7 @@ import (
 	"bufio"
 	"fmt"
 	"io"
+	"net"
 	"os"
 	"strings"
 )
@@ -17,9 +18,7 @@ func Chat(conn io.ReadWriter) {
 		b := make([]byte, bufSize)
 		for {
 			n, err := conn.Read(b)
-			if err != nil {
-				return
-			}
+			Check(err)
 			fmt.Printf("Got message: %s\n", string(b[:n]))
 		}
 	}()
@@ -38,7 +37,17 @@ func Chat(conn io.ReadWriter) {
 
 // Check is a helper to throw errors in the examples
 func Check(err error) {
-	if err != nil {
+	switch e := err.(type) {
+	case nil:
+	case (net.Error):
+		if e.Temporary() {
+			fmt.Printf("Warning: %v\n", err)
+			return
+		}
+		fmt.Printf("net.Error: %v\n", err)
+		panic(err)
+	default:
+		fmt.Printf("error: %v\n", err)
 		panic(err)
 	}
 }
diff --git a/go.mod b/go.mod
index 668984d..1a01226 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,7 @@ module github.com/pion/dtls/v2
 
 require (
 	github.com/pion/logging v0.2.2
-	github.com/pion/transport v0.8.10
+	github.com/pion/transport v0.9.1
 	golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d
 	golang.org/x/net v0.0.0-20200301022130-244492dfa37a
 	golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
diff --git a/go.sum b/go.sum
index 04f6b9e..303589c 100644
--- a/go.sum
+++ b/go.sum
@@ -1,11 +1,11 @@
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
 github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
-github.com/pion/transport v0.8.10 h1:lTiobMEw2PG6BH/mgIVqTV2mBp/mPT+IJLaN8ZxgdHk=
-github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
+github.com/pion/transport v0.9.1 h1:ubUOWYKJOEjfw8atslUz3Ay8BaybomFGbtX5LsxB2Ko=
+github.com/pion/transport v0.9.1/go.mod h1:O5wqQ7gDvcsMmTG+aIZR0ckHIRM4CmNTOzbLT1ZFA8g=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
 golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -14,6 +14,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
diff --git a/internal/replaydetector/fixedbig.go b/internal/replaydetector/fixedbig.go
deleted file mode 100644
index a571a1a..0000000
--- a/internal/replaydetector/fixedbig.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package replaydetector
-
-import (
-	"fmt"
-)
-
-// fixedBigInt is the fix-sized multi-word integer.
-type fixedBigInt struct {
-	bits    []uint64
-	n       uint
-	msbMask uint64
-}
-
-// newFixedBigInt creates a new fix-sized multi-word int.
-func newFixedBigInt(n uint) *fixedBigInt {
-	chunkSize := (n + 63) / 64
-	if chunkSize == 0 {
-		chunkSize = 1
-	}
-	return &fixedBigInt{
-		bits:    make([]uint64, chunkSize),
-		n:       n,
-		msbMask: (1 << (64 - n%64)) - 1,
-	}
-}
-
-// Lsh is the left shift operation.
-func (s *fixedBigInt) Lsh(n uint) {
-	if n == 0 {
-		return
-	}
-	nChunk := int(n / 64)
-	nN := n % 64
-
-	for i := len(s.bits) - 1; i >= 0; i-- {
-		var carry uint64
-		if i-nChunk >= 0 {
-			carry = s.bits[i-nChunk] << nN
-			if i-nChunk-1 >= 0 {
-				carry |= s.bits[i-nChunk-1] >> (64 - nN)
-			}
-		}
-		s.bits[i] = (s.bits[i] << n) | carry
-	}
-	s.bits[len(s.bits)-1] &= s.msbMask
-}
-
-// Bit returns i-th bit of the fixedBigInt.
-func (s *fixedBigInt) Bit(i uint) uint {
-	if i >= s.n {
-		return 0
-	}
-	chunk := i / 64
-	pos := i % 64
-	if s.bits[chunk]&(1<<pos) != 0 {
-		return 1
-	}
-	return 0
-}
-
-// SetBit sets i-th bit to 1.
-func (s *fixedBigInt) SetBit(i uint) {
-	if i >= s.n {
-		return
-	}
-	chunk := i / 64
-	pos := i % 64
-	s.bits[chunk] |= 1 << pos
-}
-
-// String returns string representation of fixedBigInt.
-func (s *fixedBigInt) String() string {
-	var out string
-	for i := len(s.bits) - 1; i >= 0; i-- {
-		out += fmt.Sprintf("%016X", s.bits[i])
-	}
-	return out
-}
diff --git a/internal/replaydetector/replaydetector.go b/internal/replaydetector/replaydetector.go
deleted file mode 100644
index ac283a6..0000000
--- a/internal/replaydetector/replaydetector.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Package replaydetector provides packet replay detection algorithm.
-package replaydetector
-
-// ReplayDetector is the interface of sequence replay detector.
-type ReplayDetector interface {
-	// Check returns true if given sequence number is not replayed.
-	// Call accept() to mark the packet is received properly.
-	Check(seq uint64) (accept func(), ok bool)
-}
-
-type slidingWindowDetector struct {
-	latestSeq  uint64
-	maxSeq     uint64
-	windowSize uint
-	mask       *fixedBigInt
-}
-
-// New creates ReplayDetector.
-func New(windowSize uint, maxSeq uint64) ReplayDetector {
-	return &slidingWindowDetector{
-		maxSeq:     maxSeq,
-		windowSize: windowSize,
-		mask:       newFixedBigInt(windowSize),
-	}
-}
-
-func (d *slidingWindowDetector) Check(seq uint64) (accept func(), ok bool) {
-	if seq > d.maxSeq {
-		// Exceeded upper limit.
-		return func() {}, false
-	}
-
-	if seq <= d.latestSeq {
-		if d.latestSeq > uint64(d.windowSize)+seq {
-			return func() {}, false
-		}
-		if d.mask.Bit(uint(d.latestSeq-seq)) != 0 {
-			// The sequence number is duplicated.
-			return func() {}, false
-		}
-	}
-
-	return func() {
-		if seq > d.latestSeq {
-			// Update the head of the window.
-			d.mask.Lsh(uint(seq - d.latestSeq))
-			d.latestSeq = seq
-		}
-		d.mask.SetBit(uint(d.latestSeq - seq))
-	}, true
-}
diff --git a/record_layer.go b/record_layer.go
index 7bd2932..11f32da 100644
--- a/record_layer.go
+++ b/record_layer.go
@@ -77,12 +77,12 @@ func unpackDatagram(buf []byte) ([][]byte, error) {
 
 	for offset := 0; len(buf) != offset; {
 		if len(buf)-offset <= recordLayerHeaderSize {
-			return nil, errDTLSPacketInvalidLength
+			return nil, errInvalidPacketLength
 		}
 
 		pktLen := (recordLayerHeaderSize + int(binary.BigEndian.Uint16(buf[offset+11:])))
 		if offset+pktLen > len(buf) {
-			return nil, errLengthMismatch
+			return nil, errInvalidPacketLength
 		}
 
 		out = append(out, buf[offset:offset+pktLen])
diff --git a/state.go b/state.go
index 63659b7..b3b1200 100644
--- a/state.go
+++ b/state.go
@@ -5,7 +5,7 @@ import (
 	"encoding/gob"
 	"sync/atomic"
 
-	"github.com/pion/dtls/v2/internal/replaydetector"
+	"github.com/pion/transport/replaydetector"
 )
 
 // State holds the dtls connection state and implements both encoding.BinaryMarshaler and encoding.BinaryUnmarshaler


Test patch:
diff --git a/conn_test.go b/conn_test.go
index 457e7c9..4e81c8d 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -8,6 +8,7 @@ import (
 	"crypto/x509"
 	"errors"
 	"fmt"
+	"io"
 	"net"
 	"testing"
 	"time"
@@ -85,6 +86,64 @@ func TestRoutineLeakOnClose(t *testing.T) {
 	// inboundLoop routine should not be leaked.
 }
 
+func TestReadWriteDeadline(t *testing.T) {
+	// Limit runtime in case of deadlocks
+	lim := test.TimeOut(5 * time.Second)
+	defer lim.Stop()
+
+	// Check for leaking routines
+	report := test.CheckRoutines(t)
+	defer report()
+
+	ca, cb, err := pipeMemory()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err := ca.SetDeadline(time.Unix(0, 1)); err != nil {
+		t.Fatal(err)
+	}
+	_, werr := ca.Write(make([]byte, 100))
+	if e, ok := werr.(net.Error); ok {
+		if !e.Timeout() {
+			t.Error("Deadline exceeded Write must return Timeout error")
+		}
+		if !e.Temporary() {
+			t.Error("Deadline exceeded Write must return Temporary error")
+		}
+	} else {
+		t.Error("Write must return net.Error error")
+	}
+	_, rerr := ca.Read(make([]byte, 100))
+	if e, ok := rerr.(net.Error); ok {
+		if !e.Timeout() {
+			t.Error("Deadline exceeded Read must return Timeout error")
+		}
+		if !e.Temporary() {
+			t.Error("Deadline exceeded Read must return Temporary error")
+		}
+	} else {
+		t.Error("Read must return net.Error error")
+	}
+	if err := ca.SetDeadline(time.Time{}); err != nil {
+		t.Error(err)
+	}
+
+	if err := ca.Close(); err != nil {
+		t.Error(err)
+	}
+	if err := cb.Close(); err != nil {
+		t.Error(err)
+	}
+
+	if _, err := ca.Write(make([]byte, 100)); err != ErrConnClosed {
+		t.Errorf("Write must return %v after close, got %v", ErrConnClosed, err)
+	}
+	if _, err := ca.Read(make([]byte, 100)); err != io.EOF {
+		t.Errorf("Read must return %v after close, got %v", io.EOF, err)
+	}
+}
+
 func pipeMemory() (*Conn, *Conn, error) {
 	// In memory pipe
 	ca, cb := dpipe.Pipe()
diff --git a/errors_errno_test.go b/errors_errno_test.go
new file mode 100644
index 0000000..7c567ee
--- /dev/null
+++ b/errors_errno_test.go
@@ -0,0 +1,41 @@
+// +build aix darwin dragonfly freebsd linux nacl nacljs netbsd openbsd solaris windows
+
+// For systems having syscall.Errno.
+// The build target must be same as errors_errno.go.
+
+package dtls
+
+import (
+	"net"
+	"testing"
+)
+
+func TestErrorsTemporary(t *testing.T) {
+	addrListen, errListen := net.ResolveUDPAddr("udp", "localhost:0")
+	if errListen != nil {
+		t.Fatalf("Unexpected error: %v", errListen)
+	}
+	// Server is not listening.
+	conn, errDial := net.DialUDP("udp", nil, addrListen)
+	if errDial != nil {
+		t.Fatalf("Unexpected error: %v", errDial)
+	}
+
+	_, _ = conn.Write([]byte{0x00}) // trigger
+	_, err := conn.Read(make([]byte, 10))
+	_ = conn.Close()
+
+	if err == nil {
+		t.Skip("ECONNREFUSED is not set by system")
+	}
+	ne, ok := netError(err).(net.Error)
+	if !ok {
+		t.Fatalf("netError must return net.Error")
+	}
+	if ne.Timeout() {
+		t.Errorf("%v must not be timeout error", err)
+	}
+	if !ne.Temporary() {
+		t.Errorf("%v must be temporary error", err)
+	}
+}
diff --git a/errors_test.go b/errors_test.go
new file mode 100644
index 0000000..a5a511e
--- /dev/null
+++ b/errors_test.go
@@ -0,0 +1,78 @@
+package dtls
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"testing"
+
+	"golang.org/x/xerrors"
+)
+
+func TestErrorUnwrap(t *testing.T) {
+	errExample := errors.New("an example error")
+
+	cases := []struct {
+		err          error
+		errUnwrapped []error
+	}{
+		{
+			&ErrFatal{errExample},
+			[]error{errExample},
+		},
+		{
+			&ErrTemporary{errExample},
+			[]error{errExample},
+		},
+		{
+			&ErrInternal{errExample},
+			[]error{errExample},
+		},
+		{
+			&ErrTimeout{errExample},
+			[]error{errExample},
+		},
+	}
+	for _, c := range cases {
+		c := c
+		t.Run(fmt.Sprintf("%T", c.err), func(t *testing.T) {
+			err := c.err
+			for _, unwrapped := range c.errUnwrapped {
+				e := xerrors.Unwrap(err)
+				if e != unwrapped {
+					t.Errorf("Unwrapped error is expected to be '%v', got '%v'", unwrapped, e)
+				}
+			}
+		})
+	}
+}
+
+func TestErrorNetError(t *testing.T) {
+	errExample := errors.New("an example error")
+
+	cases := []struct {
+		err                error
+		str                string
+		timeout, temporary bool
+	}{
+		{&ErrFatal{errExample}, "dtls fatal: an example error", false, false},
+		{&ErrTemporary{errExample}, "dtls temporary: an example error", false, true},
+		{&ErrInternal{errExample}, "dtls internal: an example error", false, false},
+		{&ErrTimeout{errExample}, "dtls timeout: an example error", true, true},
+	}
+	for _, c := range cases {
+		c := c
+		t.Run(fmt.Sprintf("%T", c.err), func(t *testing.T) {
+			ne, ok := c.err.(net.Error)
+			if !ok {
+				t.Fatalf("%T doesn't implement net.Error", c.err)
+			}
+			if ne.Timeout() != c.timeout {
+				t.Errorf("%T.Timeout() should be %v", c.err, c.timeout)
+			}
+			if ne.Temporary() != c.temporary {
+				t.Errorf("%T.Temporary() should be %v", c.err, c.temporary)
+			}
+		})
+	}
+}
diff --git a/internal/replaydetector/fixedbig_test.go b/internal/replaydetector/fixedbig_test.go
deleted file mode 100644
index 716a533..0000000
--- a/internal/replaydetector/fixedbig_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package replaydetector
-
-import (
-	"fmt"
-)
-
-func Example_fixedBigInt_SetBit() {
-	bi := newFixedBigInt(224)
-
-	bi.SetBit(0)
-	fmt.Println(bi.String())
-	bi.Lsh(1)
-	fmt.Println(bi.String())
-
-	bi.Lsh(0)
-	fmt.Println(bi.String())
-
-	bi.SetBit(10)
-	fmt.Println(bi.String())
-	bi.Lsh(20)
-	fmt.Println(bi.String())
-
-	bi.SetBit(80)
-	fmt.Println(bi.String())
-	bi.Lsh(4)
-	fmt.Println(bi.String())
-
-	bi.SetBit(130)
-	fmt.Println(bi.String())
-	bi.Lsh(64)
-	fmt.Println(bi.String())
-
-	bi.SetBit(7)
-	fmt.Println(bi.String())
-
-	bi.Lsh(129)
-	fmt.Println(bi.String())
-
-	for i := 0; i < 256; i++ {
-		bi.Lsh(1)
-		bi.SetBit(0)
-	}
-	fmt.Println(bi.String())
-
-	// output:
-	// 0000000000000000000000000000000000000000000000000000000000000001
-	// 0000000000000000000000000000000000000000000000000000000000000002
-	// 0000000000000000000000000000000000000000000000000000000000000002
-	// 0000000000000000000000000000000000000000000000000000000000000402
-	// 0000000000000000000000000000000000000000000000000000000040200000
-	// 0000000000000000000000000000000000000000000100000000000040200000
-	// 0000000000000000000000000000000000000000001000000000000402000000
-	// 0000000000000000000000000000000400000000001000000000000402000000
-	// 0000000000000004000000000010000000000004020000000000000000000000
-	// 0000000000000004000000000010000000000004020000000000000000000080
-	// 0000000004000000000000000000010000000000000000000000000000000000
-	// 00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-}
diff --git a/internal/replaydetector/replaydetector_test.go b/internal/replaydetector/replaydetector_test.go
deleted file mode 100644
index 4ecc59c..0000000
--- a/internal/replaydetector/replaydetector_test.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package replaydetector
-
-import (
-	"reflect"
-	"testing"
-)
-
-func TestReplayDetector(t *testing.T) {
-	const largeSeq = 0x100000000000
-	cases := map[string]struct {
-		windowSize uint
-		maxSeq     uint64
-		input      []uint64
-		valid      []bool
-		expected   []uint64
-	}{
-		"Continuous": {16, 0x0000FFFFFFFFFFFF,
-			[]uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
-			[]bool{
-				true, true, true, true, true, true, true, true, true, true,
-				true, true, true, true, true, true, true, true, true, true,
-				true,
-			},
-			[]uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
-		},
-		"ValidLargeJump": {16, 0x0000FFFFFFFFFFFF,
-			[]uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, largeSeq, 11, largeSeq + 1, largeSeq + 2, largeSeq + 3},
-			[]bool{
-				true, true, true, true, true, true, true, true, true, true,
-				true, true, true, true, true,
-			},
-			[]uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, largeSeq, largeSeq + 1, largeSeq + 2, largeSeq + 3},
-		},
-		"InvalidLargeJump": {16, 0x0000FFFFFFFFFFFF,
-			[]uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, largeSeq, 11, 12, 13, 14, 15},
-			[]bool{
-				true, true, true, true, true, true, true, true, true, true,
-				false, true, true, true, true, true,
-			},
-			[]uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15},
-		},
-		"DuplicateAfterValidJump": {196, 0x0000FFFFFFFFFFFF,
-			[]uint64{0, 1, 2, 129, 0, 1, 2},
-			[]bool{
-				true, true, true, true, true, true, true,
-			},
-			[]uint64{0, 1, 2, 129},
-		},
-		"DuplicateAfterInvalidJump": {196, 0x0000FFFFFFFFFFFF,
-			[]uint64{0, 1, 2, 128, 0, 1, 2},
-			[]bool{
-				true, true, true, false, true, true, true,
-			},
-			[]uint64{0, 1, 2},
-		},
-		"ContinuousOffset": {16, 0x0000FFFFFFFFFFFF,
-			[]uint64{100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114},
-			[]bool{
-				true, true, true, true, true, true, true, true, true, true,
-				true, true, true, true, true,
-			},
-			[]uint64{100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114},
-		},
-		"Reordered": {128, 0x0000FFFFFFFFFFFF,
-			[]uint64{96, 64, 16, 80, 32, 48, 8, 24, 88, 40, 128, 56, 72, 112, 104, 120},
-			[]bool{
-				true, true, true, true, true, true, true, true, true, true,
-				true, true, true, true, true, true,
-			},
-			[]uint64{96, 64, 16, 80, 32, 48, 8, 24, 88, 40, 128, 56, 72, 112, 104, 120},
-		},
-		"Old": {100, 0x0000FFFFFFFFFFFF,
-			[]uint64{24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 8, 16},
-			[]bool{
-				true, true, true, true, true, true, true, true, true, true,
-				true, true, true, true, true, true,
-			},
-			[]uint64{24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128},
-		},
-		"ReplayedLater": {128, 0x0000FFFFFFFFFFFF,
-			[]uint64{16, 32, 48, 64, 80, 96, 112, 128, 16, 32, 48, 64, 80, 96, 112, 128},
-			[]bool{
-				true, true, true, true, true, true, true, true, true, true,
-				true, true, true, true, true, true,
-			},
-			[]uint64{16, 32, 48, 64, 80, 96, 112, 128},
-		},
-		"ReplayedQuick": {128, 0x0000FFFFFFFFFFFF,
-			[]uint64{16, 16, 32, 32, 48, 48, 64, 64, 80, 80, 96, 96, 112, 112, 128, 128},
-			[]bool{
-				true, true, true, true, true, true, true, true, true, true,
-				true, true, true, true, true, true,
-			},
-			[]uint64{16, 32, 48, 64, 80, 96, 112, 128},
-		},
-		"Strict": {0, 0x0000FFFFFFFFFFFF,
-			[]uint64{1, 3, 2, 4, 5, 6, 7, 8, 9, 10},
-			[]bool{
-				true, true, true, true, true, true, true, true, true, true,
-			},
-			[]uint64{1, 3, 4, 5, 6, 7, 8, 9, 10},
-		},
-		"Overflow": {128, 0x0000FFFFFFFFFFFF,
-			[]uint64{0x0000FFFFFFFFFFFE, 0x0000FFFFFFFFFFFF, 0x0001000000000000, 0x0001000000000001},
-			[]bool{
-				true, true, true, true,
-			},
-			[]uint64{0x0000FFFFFFFFFFFE, 0x0000FFFFFFFFFFFF},
-		},
-	}
-	for name, c := range cases {
-		c := c
-		t.Run(name, func(t *testing.T) {
-			det := New(c.windowSize, c.maxSeq)
-			var out []uint64
-			for i, seq := range c.input {
-				accept, ok := det.Check(seq)
-				if ok {
-					if c.valid[i] {
-						out = append(out, seq)
-						accept()
-					}
-				}
-			}
-			if !reflect.DeepEqual(c.expected, out) {
-				t.Errorf("Wrong replay detection result:\nexpected: %v\ngot:      %v",
-					c.expected, out,
-				)
-			}
-		})
-	}
-}
diff --git a/record_layer_test.go b/record_layer_test.go
index f838aa8..5c757ac 100644
--- a/record_layer_test.go
+++ b/record_layer_test.go
@@ -33,12 +33,12 @@ func TestUDPDecode(t *testing.T) {
 		{
 			Name:      "Invalid packet length",
 			Data:      []byte{0x14, 0xfe},
-			WantError: errDTLSPacketInvalidLength,
+			WantError: errInvalidPacketLength,
 		},
 		{
 			Name:      "Packet declared invalid length",
 			Data:      []byte{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xFF, 0x01},
-			WantError: errLengthMismatch,
+			WantError: errInvalidPacketLength,
 		},
 	} {
 		dtlsPkts, err := unpackDatagram(test.Data)