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 }