1// Package tlv contains code to read and write type-length-value messages.
2package tlv
3
4import (
5	"encoding/binary"
6	"fmt"
7	"io"
8)
9
10// MaxMessageSize defines how large a message can be before we reject it.
11const MaxMessageSize = 1024 * 1024 * 1024 // 1GB
12
13// ReadTLV reads a type-length-value record from r.
14func ReadTLV(r io.Reader) (byte, []byte, error) {
15	typ, err := ReadType(r)
16	if err != nil {
17		return 0, nil, err
18	}
19
20	buf, err := ReadLV(r)
21	if err != nil {
22		return 0, nil, err
23	}
24	return typ, buf, err
25}
26
27// ReadType reads the type from a TLV record.
28func ReadType(r io.Reader) (byte, error) {
29	var typ [1]byte
30	if _, err := io.ReadFull(r, typ[:]); err != nil {
31		if err == io.EOF {
32			return 0, err
33		} else {
34			return 0, fmt.Errorf("read message type: %s", err)
35		}
36	}
37	return typ[0], nil
38}
39
40// ReadLV reads the length-value from a TLV record.
41func ReadLV(r io.Reader) ([]byte, error) {
42	// Read the size of the message.
43	var sz int64
44	if err := binary.Read(r, binary.BigEndian, &sz); err != nil {
45		return nil, fmt.Errorf("read message size: %s", err)
46	}
47
48	if sz < 0 {
49		return nil, fmt.Errorf("negative message size is invalid: %d", sz)
50	}
51
52	if sz >= MaxMessageSize {
53		return nil, fmt.Errorf("max message size of %d exceeded: %d", MaxMessageSize, sz)
54	}
55
56	// Read the value.
57	buf := make([]byte, sz)
58	if _, err := io.ReadFull(r, buf); err != nil {
59		return nil, fmt.Errorf("read message value: %s", err)
60	}
61
62	return buf, nil
63}
64
65// WriteTLV writes a type-length-value record to w.
66func WriteTLV(w io.Writer, typ byte, buf []byte) error {
67	if err := WriteType(w, typ); err != nil {
68		return err
69	}
70	if err := WriteLV(w, buf); err != nil {
71		return err
72	}
73	return nil
74}
75
76// WriteType writes the type in a TLV record to w.
77func WriteType(w io.Writer, typ byte) error {
78	if _, err := w.Write([]byte{typ}); err != nil {
79		return fmt.Errorf("write message type: %s", err)
80	}
81	return nil
82}
83
84// WriteLV writes the length-value in a TLV record to w.
85func WriteLV(w io.Writer, buf []byte) error {
86	// Write the size of the message.
87	if err := binary.Write(w, binary.BigEndian, int64(len(buf))); err != nil {
88		return fmt.Errorf("write message size: %s", err)
89	}
90
91	// Write the value.
92	if _, err := w.Write(buf); err != nil {
93		return fmt.Errorf("write message value: %s", err)
94	}
95	return nil
96}
97