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 4package proxyproto 5 6import ( 7 "encoding/binary" 8 "errors" 9 "fmt" 10 "math" 11) 12 13const ( 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 37var ( 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 44type PP2Type byte 45 46// TLV is a uninterpreted Type-Length-Value for V2 protocol, see section 2.2 47type TLV struct { 48 Type PP2Type 49 Value []byte 50} 51 52// SplitTLVs splits the Type-Length-Value vector, returns the vector or an error. 53func 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. 79func 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 95func (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 115func (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 120func (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 125func (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 130func (p PP2Type) Spec() bool { 131 return p.Registered() || p.App() || p.Experiment() || p.Future() 132} 133