添加 github.com/pion/dtls 代码

This commit is contained in:
bjdgyc
2021-05-21 19:03:00 +08:00
parent 54a0cb7928
commit 28b5119f50
380 changed files with 16870 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
// Package recordlayer implements the TLS Record Layer https://tools.ietf.org/html/rfc5246#section-6
package recordlayer
import (
"errors"
"github.com/pion/dtls/v2/pkg/protocol"
)
var (
errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
errInvalidPacketLength = &protocol.TemporaryError{Err: errors.New("packet length and declared length do not match")} //nolint:goerr113
errSequenceNumberOverflow = &protocol.InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113
errUnsupportedProtocolVersion = &protocol.FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113
errInvalidContentType = &protocol.TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113
)

View File

@@ -0,0 +1,61 @@
package recordlayer
import (
"encoding/binary"
"github.com/pion/dtls/v2/internal/util"
"github.com/pion/dtls/v2/pkg/protocol"
)
// Header implements a TLS RecordLayer header
type Header struct {
ContentType protocol.ContentType
ContentLen uint16
Version protocol.Version
Epoch uint16
SequenceNumber uint64 // uint48 in spec
}
// RecordLayer enums
const (
HeaderSize = 13
MaxSequenceNumber = 0x0000FFFFFFFFFFFF
)
// Marshal encodes a TLS RecordLayer Header to binary
func (h *Header) Marshal() ([]byte, error) {
if h.SequenceNumber > MaxSequenceNumber {
return nil, errSequenceNumberOverflow
}
out := make([]byte, HeaderSize)
out[0] = byte(h.ContentType)
out[1] = h.Version.Major
out[2] = h.Version.Minor
binary.BigEndian.PutUint16(out[3:], h.Epoch)
util.PutBigEndianUint48(out[5:], h.SequenceNumber)
binary.BigEndian.PutUint16(out[HeaderSize-2:], h.ContentLen)
return out, nil
}
// Unmarshal populates a TLS RecordLayer Header from binary
func (h *Header) Unmarshal(data []byte) error {
if len(data) < HeaderSize {
return errBufferTooSmall
}
h.ContentType = protocol.ContentType(data[0])
h.Version.Major = data[1]
h.Version.Minor = data[2]
h.Epoch = binary.BigEndian.Uint16(data[3:])
// SequenceNumber is stored as uint48, make into uint64
seqCopy := make([]byte, 8)
copy(seqCopy[2:], data[5:11])
h.SequenceNumber = binary.BigEndian.Uint64(seqCopy)
if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) {
return errUnsupportedProtocolVersion
}
return nil
}

View File

@@ -0,0 +1,99 @@
package recordlayer
import (
"encoding/binary"
"github.com/pion/dtls/v2/pkg/protocol"
"github.com/pion/dtls/v2/pkg/protocol/alert"
"github.com/pion/dtls/v2/pkg/protocol/handshake"
)
// RecordLayer which handles all data transport.
// The record layer is assumed to sit directly on top of some
// reliable transport such as TCP. The record layer can carry four types of content:
//
// 1. Handshake messages—used for algorithm negotiation and key establishment.
// 2. ChangeCipherSpec messages—really part of the handshake but technically a separate kind of message.
// 3. Alert messages—used to signal that errors have occurred
// 4. Application layer data
//
// The DTLS record layer is extremely similar to that of TLS 1.1. The
// only change is the inclusion of an explicit sequence number in the
// record. This sequence number allows the recipient to correctly
// verify the TLS MAC.
//
// https://tools.ietf.org/html/rfc4347#section-4.1
type RecordLayer struct {
Header Header
Content protocol.Content
}
// Marshal encodes the RecordLayer to binary
func (r *RecordLayer) Marshal() ([]byte, error) {
contentRaw, err := r.Content.Marshal()
if err != nil {
return nil, err
}
r.Header.ContentLen = uint16(len(contentRaw))
r.Header.ContentType = r.Content.ContentType()
headerRaw, err := r.Header.Marshal()
if err != nil {
return nil, err
}
return append(headerRaw, contentRaw...), nil
}
// Unmarshal populates the RecordLayer from binary
func (r *RecordLayer) Unmarshal(data []byte) error {
if len(data) < HeaderSize {
return errBufferTooSmall
}
if err := r.Header.Unmarshal(data); err != nil {
return err
}
switch protocol.ContentType(data[0]) {
case protocol.ContentTypeChangeCipherSpec:
r.Content = &protocol.ChangeCipherSpec{}
case protocol.ContentTypeAlert:
r.Content = &alert.Alert{}
case protocol.ContentTypeHandshake:
r.Content = &handshake.Handshake{}
case protocol.ContentTypeApplicationData:
r.Content = &protocol.ApplicationData{}
default:
return errInvalidContentType
}
return r.Content.Unmarshal(data[HeaderSize:])
}
// UnpackDatagram extracts all RecordLayer messages from a single datagram.
// Note that as with TLS, multiple handshake messages may be placed in
// the same DTLS record, provided that there is room and that they are
// part of the same flight. Thus, there are two acceptable ways to pack
// two DTLS messages into the same datagram: in the same record or in
// separate records.
// https://tools.ietf.org/html/rfc6347#section-4.2.3
func UnpackDatagram(buf []byte) ([][]byte, error) {
out := [][]byte{}
for offset := 0; len(buf) != offset; {
if len(buf)-offset <= HeaderSize {
return nil, errInvalidPacketLength
}
pktLen := (HeaderSize + int(binary.BigEndian.Uint16(buf[offset+11:])))
if offset+pktLen > len(buf) {
return nil, errInvalidPacketLength
}
out = append(out, buf[offset:offset+pktLen])
offset += pktLen
}
return out, nil
}

View File

@@ -0,0 +1,92 @@
package recordlayer
import (
"errors"
"reflect"
"testing"
"github.com/pion/dtls/v2/pkg/protocol"
)
func TestUDPDecode(t *testing.T) {
for _, test := range []struct {
Name string
Data []byte
Want [][]byte
WantError error
}{
{
Name: "Change Cipher Spec, single packet",
Data: []byte{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01},
Want: [][]byte{
{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01},
},
},
{
Name: "Change Cipher Spec, multi packet",
Data: []byte{
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01,
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x01,
},
Want: [][]byte{
{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01},
{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x01},
},
},
{
Name: "Invalid packet length",
Data: []byte{0x14, 0xfe},
WantError: errInvalidPacketLength,
},
{
Name: "Packet declared invalid length",
Data: []byte{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xFF, 0x01},
WantError: errInvalidPacketLength,
},
} {
dtlsPkts, err := UnpackDatagram(test.Data)
if !errors.Is(err, test.WantError) {
t.Errorf("Unexpected Error %q: exp: %v got: %v", test.Name, test.WantError, err)
} else if !reflect.DeepEqual(test.Want, dtlsPkts) {
t.Errorf("%q UDP decode: got %q, want %q", test.Name, dtlsPkts, test.Want)
}
}
}
func TestRecordLayerRoundTrip(t *testing.T) {
for _, test := range []struct {
Name string
Data []byte
Want *RecordLayer
WantMarshalError error
WantUnmarshalError error
}{
{
Name: "Change Cipher Spec, single packet",
Data: []byte{0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01},
Want: &RecordLayer{
Header: Header{
ContentType: protocol.ContentTypeChangeCipherSpec,
Version: protocol.Version{Major: 0xfe, Minor: 0xff},
Epoch: 0,
SequenceNumber: 18,
},
Content: &protocol.ChangeCipherSpec{},
},
},
} {
r := &RecordLayer{}
if err := r.Unmarshal(test.Data); !errors.Is(err, test.WantUnmarshalError) {
t.Errorf("Unexpected Error %q: exp: %v got: %v", test.Name, test.WantUnmarshalError, err)
} else if !reflect.DeepEqual(test.Want, r) {
t.Errorf("%q recordLayer.unmarshal: got %q, want %q", test.Name, r, test.Want)
}
data, marshalErr := r.Marshal()
if !errors.Is(marshalErr, test.WantMarshalError) {
t.Errorf("Unexpected Error %q: exp: %v got: %v", test.Name, test.WantMarshalError, marshalErr)
} else if !reflect.DeepEqual(test.Data, data) {
t.Errorf("%q recordLayer.marshal: got % 02x, want % 02x", test.Name, data, test.Data)
}
}
}