mirror of https://github.com/bjdgyc/anylink.git
100 lines
2.9 KiB
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
|
|
}
|