dockerfiles/anylink/dtls-2.0.9/pkg/protocol/recordlayer/recordlayer.go

100 lines
2.9 KiB
Go

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
}