[822] | 1 | // Type-Length-Value splitting and parsing for proxy protocol V2
|
---|
| 2 | // See spec https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt sections 2.2 to 2.7 and
|
---|
| 3 |
|
---|
| 4 | package proxyproto
|
---|
| 5 |
|
---|
| 6 | import (
|
---|
| 7 | "encoding/binary"
|
---|
| 8 | "errors"
|
---|
| 9 | "fmt"
|
---|
| 10 | "math"
|
---|
| 11 | )
|
---|
| 12 |
|
---|
| 13 | const (
|
---|
| 14 | // Section 2.2
|
---|
| 15 | PP2_TYPE_ALPN PP2Type = 0x01
|
---|
| 16 | PP2_TYPE_AUTHORITY PP2Type = 0x02
|
---|
| 17 | PP2_TYPE_CRC32C PP2Type = 0x03
|
---|
| 18 | PP2_TYPE_NOOP PP2Type = 0x04
|
---|
| 19 | PP2_TYPE_UNIQUE_ID PP2Type = 0x05
|
---|
| 20 | PP2_TYPE_SSL PP2Type = 0x20
|
---|
| 21 | PP2_SUBTYPE_SSL_VERSION PP2Type = 0x21
|
---|
| 22 | PP2_SUBTYPE_SSL_CN PP2Type = 0x22
|
---|
| 23 | PP2_SUBTYPE_SSL_CIPHER PP2Type = 0x23
|
---|
| 24 | PP2_SUBTYPE_SSL_SIG_ALG PP2Type = 0x24
|
---|
| 25 | PP2_SUBTYPE_SSL_KEY_ALG PP2Type = 0x25
|
---|
| 26 | PP2_TYPE_NETNS PP2Type = 0x30
|
---|
| 27 |
|
---|
| 28 | // Section 2.2.7, reserved types
|
---|
| 29 | PP2_TYPE_MIN_CUSTOM PP2Type = 0xE0
|
---|
| 30 | PP2_TYPE_MAX_CUSTOM PP2Type = 0xEF
|
---|
| 31 | PP2_TYPE_MIN_EXPERIMENT PP2Type = 0xF0
|
---|
| 32 | PP2_TYPE_MAX_EXPERIMENT PP2Type = 0xF7
|
---|
| 33 | PP2_TYPE_MIN_FUTURE PP2Type = 0xF8
|
---|
| 34 | PP2_TYPE_MAX_FUTURE PP2Type = 0xFF
|
---|
| 35 | )
|
---|
| 36 |
|
---|
| 37 | var (
|
---|
| 38 | ErrTruncatedTLV = errors.New("proxyproto: truncated TLV")
|
---|
| 39 | ErrMalformedTLV = errors.New("proxyproto: malformed TLV Value")
|
---|
| 40 | ErrIncompatibleTLV = errors.New("proxyproto: incompatible TLV type")
|
---|
| 41 | )
|
---|
| 42 |
|
---|
| 43 | // PP2Type is the proxy protocol v2 type
|
---|
| 44 | type PP2Type byte
|
---|
| 45 |
|
---|
| 46 | // TLV is a uninterpreted Type-Length-Value for V2 protocol, see section 2.2
|
---|
| 47 | type TLV struct {
|
---|
| 48 | Type PP2Type
|
---|
| 49 | Value []byte
|
---|
| 50 | }
|
---|
| 51 |
|
---|
| 52 | // SplitTLVs splits the Type-Length-Value vector, returns the vector or an error.
|
---|
| 53 | func SplitTLVs(raw []byte) ([]TLV, error) {
|
---|
| 54 | var tlvs []TLV
|
---|
| 55 | for i := 0; i < len(raw); {
|
---|
| 56 | tlv := TLV{
|
---|
| 57 | Type: PP2Type(raw[i]),
|
---|
| 58 | }
|
---|
| 59 | if len(raw)-i <= 2 {
|
---|
| 60 | return nil, ErrTruncatedTLV
|
---|
| 61 | }
|
---|
| 62 | tlvLen := int(binary.BigEndian.Uint16(raw[i+1 : i+3])) // Max length = 65K
|
---|
| 63 | i += 3
|
---|
| 64 | if i+tlvLen > len(raw) {
|
---|
| 65 | return nil, ErrTruncatedTLV
|
---|
| 66 | }
|
---|
| 67 | // Ignore no-op padding
|
---|
| 68 | if tlv.Type != PP2_TYPE_NOOP {
|
---|
| 69 | tlv.Value = make([]byte, tlvLen)
|
---|
| 70 | copy(tlv.Value, raw[i:i+tlvLen])
|
---|
| 71 | }
|
---|
| 72 | i += tlvLen
|
---|
| 73 | tlvs = append(tlvs, tlv)
|
---|
| 74 | }
|
---|
| 75 | return tlvs, nil
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | // JoinTLVs joins multiple Type-Length-Value records.
|
---|
| 79 | func JoinTLVs(tlvs []TLV) ([]byte, error) {
|
---|
| 80 | var raw []byte
|
---|
| 81 | for _, tlv := range tlvs {
|
---|
| 82 | if len(tlv.Value) > math.MaxUint16 {
|
---|
| 83 | return nil, fmt.Errorf("proxyproto: cannot format TLV %v with length %d", tlv.Type, len(tlv.Value))
|
---|
| 84 | }
|
---|
| 85 | var length [2]byte
|
---|
| 86 | binary.BigEndian.PutUint16(length[:], uint16(len(tlv.Value)))
|
---|
| 87 | raw = append(raw, byte(tlv.Type))
|
---|
| 88 | raw = append(raw, length[:]...)
|
---|
| 89 | raw = append(raw, tlv.Value...)
|
---|
| 90 | }
|
---|
| 91 | return raw, nil
|
---|
| 92 | }
|
---|
| 93 |
|
---|
| 94 | // Registered is true if the type is registered in the spec, see section 2.2
|
---|
| 95 | func (p PP2Type) Registered() bool {
|
---|
| 96 | switch p {
|
---|
| 97 | case PP2_TYPE_ALPN,
|
---|
| 98 | PP2_TYPE_AUTHORITY,
|
---|
| 99 | PP2_TYPE_CRC32C,
|
---|
| 100 | PP2_TYPE_NOOP,
|
---|
| 101 | PP2_TYPE_UNIQUE_ID,
|
---|
| 102 | PP2_TYPE_SSL,
|
---|
| 103 | PP2_SUBTYPE_SSL_VERSION,
|
---|
| 104 | PP2_SUBTYPE_SSL_CN,
|
---|
| 105 | PP2_SUBTYPE_SSL_CIPHER,
|
---|
| 106 | PP2_SUBTYPE_SSL_SIG_ALG,
|
---|
| 107 | PP2_SUBTYPE_SSL_KEY_ALG,
|
---|
| 108 | PP2_TYPE_NETNS:
|
---|
| 109 | return true
|
---|
| 110 | }
|
---|
| 111 | return false
|
---|
| 112 | }
|
---|
| 113 |
|
---|
| 114 | // App is true if the type is reserved for application specific data, see section 2.2.7
|
---|
| 115 | func (p PP2Type) App() bool {
|
---|
| 116 | return p >= PP2_TYPE_MIN_CUSTOM && p <= PP2_TYPE_MAX_CUSTOM
|
---|
| 117 | }
|
---|
| 118 |
|
---|
| 119 | // Experiment is true if the type is reserved for temporary experimental use by application developers, see section 2.2.7
|
---|
| 120 | func (p PP2Type) Experiment() bool {
|
---|
| 121 | return p >= PP2_TYPE_MIN_EXPERIMENT && p <= PP2_TYPE_MAX_EXPERIMENT
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | // Future is true is the type is reserved for future use, see section 2.2.7
|
---|
| 125 | func (p PP2Type) Future() bool {
|
---|
| 126 | return p >= PP2_TYPE_MIN_FUTURE
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | // Spec is true if the type is covered by the spec, see section 2.2 and 2.2.7
|
---|
| 130 | func (p PP2Type) Spec() bool {
|
---|
| 131 | return p.Registered() || p.App() || p.Experiment() || p.Future()
|
---|
| 132 | }
|
---|