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 | }
|
---|