1 #include "HAProxyHandler.h"
2 #include <cstring>
3 #include <cassert>
4
5 static const uint8_t haproxy_signature_v1[] = {
6 'P', 'R', 'O', 'X', 'Y', ' ', 'T', 'C', 'P'
7 };
8
9 static const uint8_t haproxy_signature_v2[] = {
10 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
11 };
12
set_error(uv_errno_t ec)13 void HAProxyHandler::set_error(uv_errno_t ec)
14 {
15 m_has_error = true;
16 m_error_code = ec;
17 }
18
consume(const uint8_t * buffer,size_t length)19 size_t HAProxyHandler::consume(const uint8_t* buffer, size_t length)
20 {
21 if(m_has_error) {
22 // Early-out if an error has been set.
23 return 0;
24 }
25
26 size_t prev_size = m_data_buf.size();
27
28 m_data_buf.insert(m_data_buf.end(), buffer, buffer + length);
29
30 size_t hdr_size = parse_proxy_block();
31
32 if(hdr_size == 0) {
33 // Not a valid PROXY block.
34 set_error(UV_EPROTO);
35 return 0;
36 }
37
38 if(hdr_size < m_data_buf.size()) {
39 // Returns how many bytes we consumed if the proxy block is over capacity.
40 return hdr_size - prev_size;
41 }
42 else if(hdr_size == m_data_buf.size()) {
43 // Consumed exactly as many bytes as we were given in the call for the header.
44 return 0;
45 }
46
47 return length;
48 }
49
parse_proxy_block()50 size_t HAProxyHandler::parse_proxy_block()
51 {
52 if(m_data_buf.size() < HAPROXY_HEADER_MIN) {
53 // We need to have at least HAPROXY_HEADER_MIN bytes in our buffer to estimate size.
54 return HAPROXY_HEADER_MIN;
55 }
56
57 if(!memcmp(&m_data_buf[0], haproxy_signature_v2, sizeof(haproxy_signature_v2))) {
58 // PROXY v2 header: We can easily determine the header size with just HAPROXY_HEADER_MIN bytes.
59 return parse_v2_block();
60 } else if(!memcmp(&m_data_buf[0], haproxy_signature_v1, sizeof(haproxy_signature_v1))) {
61 // PROXY v1 header: This is the human-readable (ASCII) form of the HAProxy header.
62 // We need to keep returning m_data_buf.size() + 2 (or m_data_buf.size() + 1 if a CR character is already at the end of the buffer)
63 return parse_v1_block();
64 }
65
66 // The data passed doesn't reflect that of a valid PROXYv1/PROXYv2 header, we should never hit this.
67 // Signal this back to consume()
68 return 0;
69 }
70
parse_v2_block()71 size_t HAProxyHandler::parse_v2_block()
72 {
73 assert(!(m_data_buf.size() < HAPROXY_HEADER_MIN));
74
75 size_t body_len = (m_data_buf[14] << 8) | m_data_buf[15];
76 size_t total_len = HAPROXY_HEADER_MIN + body_len;
77
78 if(m_data_buf.size() < total_len) {
79 // We don't have enough bytes for the PROXYv2 block yet.
80 return total_len;
81 }
82
83 int version = (m_data_buf[12] >> 4) & 0xF;
84 int command = (m_data_buf[12]) & 0xF;
85 int family = (m_data_buf[13] >> 4) & 0xF;
86 int transp = (m_data_buf[13]) & 0xF;
87
88 if(version != 2) {
89 set_error(UV_EPROTO);
90 return 0;
91 }
92
93 if(family != 0x1 && family != 0x2) {
94 set_error(UV_EAFNOSUPPORT);
95 return 0;
96 }
97
98 if(transp != 0x1) {
99 set_error(UV_EPROTONOSUPPORT);
100 return 0;
101 }
102
103 if(command == 0x0) {
104 // LOCAL, ignore the body:
105 m_is_local = true;
106 return total_len;
107 }
108
109 if(command != 0x1) {
110 set_error(UV_EPROTO);
111 return 0;
112 }
113
114 if((family == 0x1 && body_len < 12) ||
115 (family == 0x2 && body_len < 36)) {
116 set_error(UV_EPROTO);
117 return 0;
118 }
119
120 char* remote_address = NULL;
121 char* local_address = NULL;
122 int port_offset = HAPROXY_HEADER_MIN;
123
124 if(family == 0x1) {
125 remote_address = new char[INET_ADDRSTRLEN];
126 local_address = new char[INET_ADDRSTRLEN];
127 uv_inet_ntop(AF_INET, &m_data_buf[HAPROXY_HEADER_MIN], remote_address, INET_ADDRSTRLEN);
128 uv_inet_ntop(AF_INET, &m_data_buf[HAPROXY_HEADER_MIN + 4], local_address, INET_ADDRSTRLEN);
129 port_offset += 8;
130 } else {
131 remote_address = new char[INET6_ADDRSTRLEN];
132 local_address = new char[INET6_ADDRSTRLEN];
133 uv_inet_ntop(AF_INET6, &m_data_buf[HAPROXY_HEADER_MIN], remote_address, INET6_ADDRSTRLEN);
134 uv_inet_ntop(AF_INET6, &m_data_buf[HAPROXY_HEADER_MIN + 16], local_address, INET6_ADDRSTRLEN);
135 port_offset += 32;
136 }
137
138 int remote_port = m_data_buf[port_offset] << 8 | m_data_buf[port_offset + 1];
139 int local_port = m_data_buf[port_offset + 2] << 8 | m_data_buf[port_offset + 3];
140
141 m_remote.ip = std::string(remote_address);
142 m_remote.port = remote_port;
143 m_local.ip = std::string(local_address);
144 m_local.port = local_port;
145
146 delete[] remote_address;
147 delete[] local_address;
148
149 // Store TLVs into our own internal buffer, to be copied by NetworkClient for CA-sided consumption.
150 size_t tlv_off = port_offset + 4;
151 ssize_t tlv_size = (total_len - tlv_off);
152 if(tlv_size > 0) {
153 m_tlv_buf = std::vector<uint8_t>(&m_data_buf[tlv_off], &m_data_buf[tlv_off + tlv_size]);
154 }
155
156 return total_len;
157 }
158
parse_v1_block()159 size_t HAProxyHandler::parse_v1_block()
160 {
161 assert(!(m_data_buf.size() < HAPROXY_HEADER_MIN));
162
163 size_t capped_length = m_data_buf.size() > HAPROXY_HEADER_MAX ? HAPROXY_HEADER_MAX : m_data_buf.size();
164
165 char* cr_chr = (char*)memchr(&m_data_buf[0], '\r', capped_length);
166
167 if(cr_chr == NULL) {
168 if(m_data_buf.size() >= HAPROXY_HEADER_MAX - 1) {
169 // We should have a CR by now, if we don't we're not dealing with a valid PROXYv1 block as per the spec.
170 set_error(UV_EPROTO);
171 return 0;
172 }
173
174 // We need *at least* 2 more bytes for the header to be complete.
175 return m_data_buf.size() + 2;
176 }
177
178 size_t cr_off = (cr_chr - (char*)&m_data_buf[0]) + 1;
179
180 if(cr_off > HAPROXY_HEADER_MAX - 1) {
181 // If the CR is after the 106th character in our buffer, we probably stepped into application-layer data.
182 set_error(UV_EPROTO);
183 return 0;
184 }
185
186 char* lf_chr = (char*)memchr(&m_data_buf[0], '\n', capped_length);
187 if(lf_chr == NULL || lf_chr != cr_chr + 1) {
188 if(m_data_buf.size() >= HAPROXY_HEADER_MAX) {
189 // We should have an LF character located after our CR by now.
190 set_error(UV_EPROTO);
191 return 0;
192 }
193
194 // Either there is no line-feed character, or for whatever reason it isn't located after a CR.
195 // Only expect one more byte.
196 return m_data_buf.size() + 1;
197 }
198
199 // (header_end_ptr - header_beg_ptr) + 1 is our final PROXYv1 header size.
200 size_t hdr_size = (lf_chr - (char*)&m_data_buf[0]) + 1;
201
202 if(hdr_size > HAPROXY_HEADER_MAX) {
203 // Our CRLF is after the maximum bounds of the expected HAProxy header size.
204 set_error(UV_EPROTO);
205 return 0;
206 }
207
208 // Our header obviously cannot be larger than the buffer it originates from.
209 assert(!(m_data_buf.size() < hdr_size));
210
211 // Scratch-buffer for strtok() related operations.
212 char* header_buf = new char[hdr_size + 1];
213 memcpy(header_buf, &m_data_buf[0], hdr_size);
214 header_buf[hdr_size] = '\0';
215
216 // Ignore the return value of the first strtok() call: It's just gonna be "PROXY"
217 strtok(header_buf, " ");
218 const char *tcp = strtok(NULL, " ");
219 const char *srcip = strtok(NULL, " ");
220 const char *dstip = strtok(NULL, " ");
221 const char *srcport = strtok(NULL, " ");
222 const char *dstport = strtok(NULL, "\r");
223
224 if(dstport == nullptr) {
225 set_error(UV_EPROTO);
226 return 0;
227 }
228
229 if(strcmp(tcp, "TCP4") != 0 && strcmp(tcp, "TCP6") != 0) {
230 set_error(UV_EPROTO);
231 return 0;
232 }
233
234 m_remote.ip = std::string(srcip);
235 m_remote.port = atoi(srcport);
236 m_local.ip = std::string(dstip);
237 m_local.port = atoi(dstport);
238
239 delete[] header_buf;
240
241 return hdr_size;
242 }
243
244