github.com/pion/dtls/v2@v2.2.12/pkg/protocol/recordlayer/recordlayer.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  package recordlayer
     5  
     6  import (
     7  	"encoding/binary"
     8  
     9  	"github.com/pion/dtls/v2/pkg/protocol"
    10  	"github.com/pion/dtls/v2/pkg/protocol/alert"
    11  	"github.com/pion/dtls/v2/pkg/protocol/handshake"
    12  )
    13  
    14  // RecordLayer which handles all data transport.
    15  // The record layer is assumed to sit directly on top of some
    16  // reliable transport such as TCP. The record layer can carry four types of content:
    17  //
    18  // 1. Handshake messages—used for algorithm negotiation and key establishment.
    19  // 2. ChangeCipherSpec messages—really part of the handshake but technically a separate kind of message.
    20  // 3. Alert messages—used to signal that errors have occurred
    21  // 4. Application layer data
    22  //
    23  // The DTLS record layer is extremely similar to that of TLS 1.1.  The
    24  // only change is the inclusion of an explicit sequence number in the
    25  // record.  This sequence number allows the recipient to correctly
    26  // verify the TLS MAC.
    27  //
    28  // https://tools.ietf.org/html/rfc4347#section-4.1
    29  type RecordLayer struct {
    30  	Header  Header
    31  	Content protocol.Content
    32  }
    33  
    34  // Marshal encodes the RecordLayer to binary
    35  func (r *RecordLayer) Marshal() ([]byte, error) {
    36  	contentRaw, err := r.Content.Marshal()
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	r.Header.ContentLen = uint16(len(contentRaw))
    42  	r.Header.ContentType = r.Content.ContentType()
    43  
    44  	headerRaw, err := r.Header.Marshal()
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	return append(headerRaw, contentRaw...), nil
    50  }
    51  
    52  // Unmarshal populates the RecordLayer from binary
    53  func (r *RecordLayer) Unmarshal(data []byte) error {
    54  	if len(data) < HeaderSize {
    55  		return errBufferTooSmall
    56  	}
    57  	if err := r.Header.Unmarshal(data); err != nil {
    58  		return err
    59  	}
    60  
    61  	switch protocol.ContentType(data[0]) {
    62  	case protocol.ContentTypeChangeCipherSpec:
    63  		r.Content = &protocol.ChangeCipherSpec{}
    64  	case protocol.ContentTypeAlert:
    65  		r.Content = &alert.Alert{}
    66  	case protocol.ContentTypeHandshake:
    67  		r.Content = &handshake.Handshake{}
    68  	case protocol.ContentTypeApplicationData:
    69  		r.Content = &protocol.ApplicationData{}
    70  	default:
    71  		return errInvalidContentType
    72  	}
    73  
    74  	return r.Content.Unmarshal(data[HeaderSize:])
    75  }
    76  
    77  // UnpackDatagram extracts all RecordLayer messages from a single datagram.
    78  // Note that as with TLS, multiple handshake messages may be placed in
    79  // the same DTLS record, provided that there is room and that they are
    80  // part of the same flight.  Thus, there are two acceptable ways to pack
    81  // two DTLS messages into the same datagram: in the same record or in
    82  // separate records.
    83  // https://tools.ietf.org/html/rfc6347#section-4.2.3
    84  func UnpackDatagram(buf []byte) ([][]byte, error) {
    85  	out := [][]byte{}
    86  
    87  	for offset := 0; len(buf) != offset; {
    88  		if len(buf)-offset <= HeaderSize {
    89  			return nil, ErrInvalidPacketLength
    90  		}
    91  
    92  		pktLen := (HeaderSize + int(binary.BigEndian.Uint16(buf[offset+11:])))
    93  		if offset+pktLen > len(buf) {
    94  			return nil, ErrInvalidPacketLength
    95  		}
    96  
    97  		out = append(out, buf[offset:offset+pktLen])
    98  		offset += pktLen
    99  	}
   100  
   101  	return out, nil
   102  }