1 | package proxyproto
|
---|
2 |
|
---|
3 | import (
|
---|
4 | "bufio"
|
---|
5 | "bytes"
|
---|
6 | "fmt"
|
---|
7 | "net"
|
---|
8 | "net/netip"
|
---|
9 | "strconv"
|
---|
10 | "strings"
|
---|
11 | )
|
---|
12 |
|
---|
13 | const (
|
---|
14 | crlf = "\r\n"
|
---|
15 | separator = " "
|
---|
16 | )
|
---|
17 |
|
---|
18 | func initVersion1() *Header {
|
---|
19 | header := new(Header)
|
---|
20 | header.Version = 1
|
---|
21 | // Command doesn't exist in v1
|
---|
22 | header.Command = PROXY
|
---|
23 | return header
|
---|
24 | }
|
---|
25 |
|
---|
26 | func parseVersion1(reader *bufio.Reader) (*Header, error) {
|
---|
27 | //The header cannot be more than 107 bytes long. Per spec:
|
---|
28 | //
|
---|
29 | // (...)
|
---|
30 | // - worst case (optional fields set to 0xff) :
|
---|
31 | // "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
|
---|
32 | // => 5 + 1 + 7 + 1 + 39 + 1 + 39 + 1 + 5 + 1 + 5 + 2 = 107 chars
|
---|
33 | //
|
---|
34 | // So a 108-byte buffer is always enough to store all the line and a
|
---|
35 | // trailing zero for string processing.
|
---|
36 | //
|
---|
37 | // It must also be CRLF terminated, as above. The header does not otherwise
|
---|
38 | // contain a CR or LF byte.
|
---|
39 | //
|
---|
40 | // ISSUE #69
|
---|
41 | // We can't use Peek here as it will block trying to fill the buffer, which
|
---|
42 | // will never happen if the header is TCP4 or TCP6 (max. 56 and 104 bytes
|
---|
43 | // respectively) and the server is expected to speak first.
|
---|
44 | //
|
---|
45 | // Similarly, we can't use ReadString or ReadBytes as these will keep reading
|
---|
46 | // until the delimiter is found; an abusive client could easily disrupt a
|
---|
47 | // server by sending a large amount of data that do not contain a LF byte.
|
---|
48 | // Another means of attack would be to start connections and simply not send
|
---|
49 | // data after the initial PROXY signature bytes, accumulating a large
|
---|
50 | // number of blocked goroutines on the server. ReadSlice will also block for
|
---|
51 | // a delimiter when the internal buffer does not fill up.
|
---|
52 | //
|
---|
53 | // A plain Read is also problematic since we risk reading past the end of the
|
---|
54 | // header without being able to easily put the excess bytes back into the reader's
|
---|
55 | // buffer (with the current implementation's design).
|
---|
56 | //
|
---|
57 | // So we use a ReadByte loop, which solves the overflow problem and avoids
|
---|
58 | // reading beyond the end of the header. However, we need one more trick to harden
|
---|
59 | // against partial header attacks (slow loris) - per spec:
|
---|
60 | //
|
---|
61 | // (..) The sender must always ensure that the header is sent at once, so that
|
---|
62 | // the transport layer maintains atomicity along the path to the receiver. The
|
---|
63 | // receiver may be tolerant to partial headers or may simply drop the connection
|
---|
64 | // when receiving a partial header. Recommendation is to be tolerant, but
|
---|
65 | // implementation constraints may not always easily permit this.
|
---|
66 | //
|
---|
67 | // We are subject to such implementation constraints. So we return an error if
|
---|
68 | // the header cannot be fully extracted with a single read of the underlying
|
---|
69 | // reader.
|
---|
70 | buf := make([]byte, 0, 107)
|
---|
71 | for {
|
---|
72 | b, err := reader.ReadByte()
|
---|
73 | if err != nil {
|
---|
74 | return nil, fmt.Errorf(ErrCantReadVersion1Header.Error()+": %v", err)
|
---|
75 | }
|
---|
76 | buf = append(buf, b)
|
---|
77 | if b == '\n' {
|
---|
78 | // End of header found
|
---|
79 | break
|
---|
80 | }
|
---|
81 | if len(buf) == 107 {
|
---|
82 | // No delimiter in first 107 bytes
|
---|
83 | return nil, ErrVersion1HeaderTooLong
|
---|
84 | }
|
---|
85 | if reader.Buffered() == 0 {
|
---|
86 | // Header was not buffered in a single read. Since we can't
|
---|
87 | // differentiate between genuine slow writers and DoS agents,
|
---|
88 | // we abort. On healthy networks, this should never happen.
|
---|
89 | return nil, ErrCantReadVersion1Header
|
---|
90 | }
|
---|
91 | }
|
---|
92 |
|
---|
93 | // Check for CR before LF.
|
---|
94 | if len(buf) < 2 || buf[len(buf)-2] != '\r' {
|
---|
95 | return nil, ErrLineMustEndWithCrlf
|
---|
96 | }
|
---|
97 |
|
---|
98 | // Check full signature.
|
---|
99 | tokens := strings.Split(string(buf[:len(buf)-2]), separator)
|
---|
100 |
|
---|
101 | // Expect at least 2 tokens: "PROXY" and the transport protocol.
|
---|
102 | if len(tokens) < 2 {
|
---|
103 | return nil, ErrCantReadAddressFamilyAndProtocol
|
---|
104 | }
|
---|
105 |
|
---|
106 | // Read address family and protocol
|
---|
107 | var transportProtocol AddressFamilyAndProtocol
|
---|
108 | switch tokens[1] {
|
---|
109 | case "TCP4":
|
---|
110 | transportProtocol = TCPv4
|
---|
111 | case "TCP6":
|
---|
112 | transportProtocol = TCPv6
|
---|
113 | case "UNKNOWN":
|
---|
114 | transportProtocol = UNSPEC // doesn't exist in v1 but fits UNKNOWN
|
---|
115 | default:
|
---|
116 | return nil, ErrCantReadAddressFamilyAndProtocol
|
---|
117 | }
|
---|
118 |
|
---|
119 | // Expect 6 tokens only when UNKNOWN is not present.
|
---|
120 | if transportProtocol != UNSPEC && len(tokens) < 6 {
|
---|
121 | return nil, ErrCantReadAddressFamilyAndProtocol
|
---|
122 | }
|
---|
123 |
|
---|
124 | // When a signature is found, allocate a v1 header with Command set to PROXY.
|
---|
125 | // Command doesn't exist in v1 but set it for other parts of this library
|
---|
126 | // to rely on it for determining connection details.
|
---|
127 | header := initVersion1()
|
---|
128 |
|
---|
129 | // Transport protocol has been processed already.
|
---|
130 | header.TransportProtocol = transportProtocol
|
---|
131 |
|
---|
132 | // When UNKNOWN, set the command to LOCAL and return early
|
---|
133 | if header.TransportProtocol == UNSPEC {
|
---|
134 | header.Command = LOCAL
|
---|
135 | return header, nil
|
---|
136 | }
|
---|
137 |
|
---|
138 | // Otherwise, continue to read addresses and ports
|
---|
139 | sourceIP, err := parseV1IPAddress(header.TransportProtocol, tokens[2])
|
---|
140 | if err != nil {
|
---|
141 | return nil, err
|
---|
142 | }
|
---|
143 | destIP, err := parseV1IPAddress(header.TransportProtocol, tokens[3])
|
---|
144 | if err != nil {
|
---|
145 | return nil, err
|
---|
146 | }
|
---|
147 | sourcePort, err := parseV1PortNumber(tokens[4])
|
---|
148 | if err != nil {
|
---|
149 | return nil, err
|
---|
150 | }
|
---|
151 | destPort, err := parseV1PortNumber(tokens[5])
|
---|
152 | if err != nil {
|
---|
153 | return nil, err
|
---|
154 | }
|
---|
155 | header.SourceAddr = &net.TCPAddr{
|
---|
156 | IP: sourceIP,
|
---|
157 | Port: sourcePort,
|
---|
158 | }
|
---|
159 | header.DestinationAddr = &net.TCPAddr{
|
---|
160 | IP: destIP,
|
---|
161 | Port: destPort,
|
---|
162 | }
|
---|
163 |
|
---|
164 | return header, nil
|
---|
165 | }
|
---|
166 |
|
---|
167 | func (header *Header) formatVersion1() ([]byte, error) {
|
---|
168 | // As of version 1, only "TCP4" ( \x54 \x43 \x50 \x34 ) for TCP over IPv4,
|
---|
169 | // and "TCP6" ( \x54 \x43 \x50 \x36 ) for TCP over IPv6 are allowed.
|
---|
170 | var proto string
|
---|
171 | switch header.TransportProtocol {
|
---|
172 | case TCPv4:
|
---|
173 | proto = "TCP4"
|
---|
174 | case TCPv6:
|
---|
175 | proto = "TCP6"
|
---|
176 | default:
|
---|
177 | // Unknown connection (short form)
|
---|
178 | return []byte("PROXY UNKNOWN" + crlf), nil
|
---|
179 | }
|
---|
180 |
|
---|
181 | sourceAddr, sourceOK := header.SourceAddr.(*net.TCPAddr)
|
---|
182 | destAddr, destOK := header.DestinationAddr.(*net.TCPAddr)
|
---|
183 | if !sourceOK || !destOK {
|
---|
184 | return nil, ErrInvalidAddress
|
---|
185 | }
|
---|
186 |
|
---|
187 | sourceIP, destIP := sourceAddr.IP, destAddr.IP
|
---|
188 | switch header.TransportProtocol {
|
---|
189 | case TCPv4:
|
---|
190 | sourceIP = sourceIP.To4()
|
---|
191 | destIP = destIP.To4()
|
---|
192 | case TCPv6:
|
---|
193 | sourceIP = sourceIP.To16()
|
---|
194 | destIP = destIP.To16()
|
---|
195 | }
|
---|
196 | if sourceIP == nil || destIP == nil {
|
---|
197 | return nil, ErrInvalidAddress
|
---|
198 | }
|
---|
199 |
|
---|
200 | buf := bytes.NewBuffer(make([]byte, 0, 108))
|
---|
201 | buf.Write(SIGV1)
|
---|
202 | buf.WriteString(separator)
|
---|
203 | buf.WriteString(proto)
|
---|
204 | buf.WriteString(separator)
|
---|
205 | buf.WriteString(sourceIP.String())
|
---|
206 | buf.WriteString(separator)
|
---|
207 | buf.WriteString(destIP.String())
|
---|
208 | buf.WriteString(separator)
|
---|
209 | buf.WriteString(strconv.Itoa(sourceAddr.Port))
|
---|
210 | buf.WriteString(separator)
|
---|
211 | buf.WriteString(strconv.Itoa(destAddr.Port))
|
---|
212 | buf.WriteString(crlf)
|
---|
213 |
|
---|
214 | return buf.Bytes(), nil
|
---|
215 | }
|
---|
216 |
|
---|
217 | func parseV1PortNumber(portStr string) (int, error) {
|
---|
218 | port, err := strconv.Atoi(portStr)
|
---|
219 | if err != nil || port < 0 || port > 65535 {
|
---|
220 | return 0, ErrInvalidPortNumber
|
---|
221 | }
|
---|
222 | return port, nil
|
---|
223 | }
|
---|
224 |
|
---|
225 | func parseV1IPAddress(protocol AddressFamilyAndProtocol, addrStr string) (net.IP, error) {
|
---|
226 | addr, err := netip.ParseAddr(addrStr)
|
---|
227 | if err != nil {
|
---|
228 | return nil, ErrInvalidAddress
|
---|
229 | }
|
---|
230 |
|
---|
231 | switch protocol {
|
---|
232 | case TCPv4:
|
---|
233 | if addr.Is4() {
|
---|
234 | return net.IP(addr.AsSlice()), nil
|
---|
235 | }
|
---|
236 | case TCPv6:
|
---|
237 | if addr.Is6() || addr.Is4In6() {
|
---|
238 | return net.IP(addr.AsSlice()), nil
|
---|
239 | }
|
---|
240 | }
|
---|
241 |
|
---|
242 | return nil, ErrInvalidAddress
|
---|
243 | }
|
---|