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