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