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