66e6acdb1c27661157bdfd6c312010d216b12943
[packages.git] / net / haproxy / patches / 001-haproxy-1.4.x-sendproxy.patch
1 From af2038557a14bf6e2915bed545e216a0f1a95fc5 Mon Sep 17 00:00:00 2001
2 From: =?UTF-8?q?Cyril=20Bont=C3=A9?= <cyril.bonte@free.fr>
3 Date: Mon, 15 Apr 2013 22:05:00 +0200
4 Subject: [PATCH] Proxy Protocol based on haproxy 1.4.23
5
6 ---
7  doc/configuration.txt     |  26 ++++++-
8  include/common/standard.h |  25 ++++++-
9  include/proto/client.h    |   1 +
10  include/types/buffers.h   |  20 ++---
11  include/types/protocols.h |   1 +
12  src/cfgparse.c            |  15 +++-
13  src/client.c              | 186 ++++++++++++++++++++++++++++++++++++++++++++++
14  src/proto_http.c          |   4 +-
15  src/session.c             |   7 ++
16  src/standard.c            |   9 ++-
17  10 files changed, 275 insertions(+), 19 deletions(-)
18
19 --- a/doc/configuration.txt
20 +++ b/doc/configuration.txt
21 @@ -1343,6 +1343,7 @@ bind [<address>]:<port_range> [, ...] tr
22  bind [<address>]:<port_range> [, ...] id <id>
23  bind [<address>]:<port_range> [, ...] name <name>
24  bind [<address>]:<port_range> [, ...] defer-accept
25 +bind [<address>]:<port_range> [, ...] accept-proxy
26    Define one or several listening addresses and/or ports in a frontend.
27    May be used in sections :   defaults | frontend | listen | backend
28                                    no   |    yes   |   yes  |   no
29 @@ -1423,6 +1424,19 @@ bind [<address>]:<port_range> [, ...] de
30                    with front firewalls which would see an established
31                    connection while the proxy will only see it in SYN_RECV.
32  
33 +    accept-proxy  is an optional keyword which enforces use of the PROXY
34 +                  protocol over any connection accepted by this listener. The
35 +                  PROXY protocol dictates the layer 3/4 addresses of the
36 +                  incoming connection to be used everywhere an address is used,
37 +                  with the only exception of "tcp-request connection" rules
38 +                  which will only see the real connection address. Logs will
39 +                  reflect the addresses indicated in the protocol, unless it is
40 +                  violated, in which case the real address will still be used.
41 +                  This keyword combined with support from external components
42 +                  can be used as an efficient and reliable alternative to the
43 +                  X-Forwarded-For mechanism which is not always reliable and
44 +                  not even always usable.
45 +
46    It is possible to specify a list of address:port combinations delimited by
47    commas. The frontend will then listen on all of these addresses. There is no
48    fixed limit to the number of addresses and ports which can be listened on in
49 @@ -1433,8 +1447,10 @@ bind [<address>]:<port_range> [, ...] de
50          listen http_proxy
51              bind :80,:443
52              bind 10.0.0.1:10080,10.0.0.1:10443
53 +            bind 127.0.0.1:8443 accept-proxy
54  
55 -  See also : "source".
56 +  See also : "source", "option forwardfor" and the PROXY protocol
57 +             documentation.
58  
59  
60  bind-process [ all | odd | even | <number 1-32> ] ...
61 @@ -7237,7 +7253,9 @@ marked with a star ('*') after the field
62  
63  Detailed fields description :
64    - "client_ip" is the IP address of the client which initiated the TCP
65 -    connection to haproxy.
66 +    connection to haproxy. Note that when the connection is accepted on a
67 +    socket configured with "accept-proxy" and the PROXY protocol is correctly
68 +    used, then the logs will reflect the forwarded connection's information.
69  
70    - "client_port" is the TCP port of the client which initiated the connection.
71  
72 @@ -7410,7 +7428,9 @@ with a star ('*') after the field name b
73  
74  Detailed fields description :
75    - "client_ip" is the IP address of the client which initiated the TCP
76 -    connection to haproxy.
77 +    connection to haproxy. Note that when the connection is accepted on a
78 +    socket configured with "accept-proxy" and the PROXY protocol is correctly
79 +    used, then the logs will reflect the forwarded connection's information.
80  
81    - "client_port" is the TCP port of the client which initiated the connection.
82  
83 --- a/include/common/standard.h
84 +++ b/include/common/standard.h
85 @@ -269,6 +269,28 @@ static inline unsigned int __strl2uic(co
86         return i;
87  }
88  
89 +/* This function reads an unsigned integer from the string pointed to by <s>
90 + * and returns it. The <s> pointer is adjusted to point to the first unread
91 + * char. The function automatically stops at <end>.
92 + */
93 +static inline unsigned int __read_uint(const char **s, const char *end)
94 +{
95 +       const char *ptr = *s;
96 +       unsigned int i = 0;
97 +       unsigned int j, k;
98 +
99 +       while (ptr < end) {
100 +               j = *ptr - '0';
101 +               k = i * 10;
102 +               if (j > 9)
103 +                       break;
104 +               i = k + j;
105 +               ptr++;
106 +       }
107 +       *s = ptr;
108 +       return i;
109 +}
110 +
111  extern unsigned int str2ui(const char *s);
112  extern unsigned int str2uic(const char *s);
113  extern unsigned int strl2ui(const char *s, int len);
114 @@ -276,9 +298,10 @@ extern unsigned int strl2uic(const char 
115  extern int strl2ic(const char *s, int len);
116  extern int strl2irc(const char *s, int len, int *ret);
117  extern int strl2llrc(const char *s, int len, long long *ret);
118 +extern unsigned int read_uint(const char **s, const char *end);
119  unsigned int inetaddr_host(const char *text);
120  unsigned int inetaddr_host_lim(const char *text, const char *stop);
121 -unsigned int inetaddr_host_lim_ret(const char *text, char *stop, const char **ret);
122 +unsigned int inetaddr_host_lim_ret(char *text, char *stop, char **ret);
123  
124  static inline char *cut_crlf(char *s) {
125  
126 --- a/include/proto/client.h
127 +++ b/include/proto/client.h
128 @@ -25,6 +25,7 @@
129  #include <common/config.h>
130  #include <types/session.h>
131  
132 +int frontend_decode_proxy_request(struct session *s, struct buffer *req, int an_bit);
133  void get_frt_addr(struct session *s);
134  int event_accept(int fd);
135  
136 --- a/include/types/buffers.h
137 +++ b/include/types/buffers.h
138 @@ -135,16 +135,16 @@
139   * The field is blanked by buffer_init() and only by analysers themselves
140   * afterwards.
141   */
142 -#define AN_REQ_INSPECT          0x00000001  /* inspect request contents */
143 -#define AN_REQ_WAIT_HTTP        0x00000002  /* wait for an HTTP request */
144 -#define AN_REQ_HTTP_PROCESS_FE  0x00000004  /* process the frontend's HTTP part */
145 -#define AN_REQ_SWITCHING_RULES  0x00000008  /* apply the switching rules */
146 -#define AN_REQ_HTTP_PROCESS_BE  0x00000010  /* process the backend's HTTP part */
147 -#define AN_REQ_HTTP_INNER       0x00000020  /* inner processing of HTTP request */
148 -#define AN_REQ_HTTP_TARPIT      0x00000040  /* wait for end of HTTP tarpit */
149 -#define AN_REQ_HTTP_BODY        0x00000080  /* inspect HTTP request body */
150 -#define AN_REQ_STICKING_RULES   0x00000100  /* table persistence matching */
151 -/* unused: 0x200 */
152 +#define AN_REQ_DECODE_PROXY     0x00000001  /* take the proxied address from a 'PROXY' line */
153 +#define AN_REQ_INSPECT          0x00000002  /* inspect request contents */
154 +#define AN_REQ_WAIT_HTTP        0x00000004  /* wait for an HTTP request */
155 +#define AN_REQ_HTTP_PROCESS_FE  0x00000008  /* process the frontend's HTTP part */
156 +#define AN_REQ_SWITCHING_RULES  0x00000010  /* apply the switching rules */
157 +#define AN_REQ_HTTP_PROCESS_BE  0x00000020  /* process the backend's HTTP part */
158 +#define AN_REQ_HTTP_INNER       0x00000040  /* inner processing of HTTP request */
159 +#define AN_REQ_HTTP_TARPIT      0x00000080  /* wait for end of HTTP tarpit */
160 +#define AN_REQ_HTTP_BODY        0x00000100  /* inspect HTTP request body */
161 +#define AN_REQ_STICKING_RULES   0x00000200  /* table persistence matching */
162  #define AN_REQ_PRST_RDP_COOKIE  0x00000400  /* persistence on rdp cookie */
163  #define AN_REQ_HTTP_XFER_BODY   0x00000800  /* forward request body */
164  
165 --- a/include/types/protocols.h
166 +++ b/include/types/protocols.h
167 @@ -72,6 +72,7 @@
168  #define LI_O_FOREIGN   0x0002  /* permit listening on foreing addresses */
169  #define LI_O_NOQUICKACK        0x0004  /* disable quick ack of immediate data (linux) */
170  #define LI_O_DEF_ACCEPT        0x0008  /* wait up to 1 second for data before accepting */
171 +#define LI_O_ACC_PROXY  0x0010  /* find the proxied address in the first request line */
172  
173  /* The listener will be directly referenced by the fdtab[] which holds its
174   * socket. The listener provides the protocol-specific accept() function to
175 --- a/src/cfgparse.c
176 +++ b/src/cfgparse.c
177 @@ -1467,6 +1467,16 @@ int cfg_parse_listen(const char *file, i
178  #endif
179                         }
180  
181 +                       if (!strcmp(args[cur_arg], "accept-proxy")) { /* expect a 'PROXY' line first */
182 +                               struct listener *l;
183 +
184 +                               for (l = curproxy->listen; l != last_listen; l = l->next)
185 +                                       l->options |= LI_O_ACC_PROXY;
186 +
187 +                               cur_arg ++;
188 +                               continue;
189 +                       }
190 +
191                         if (!strcmp(args[cur_arg], "name")) {
192                                 struct listener *l;
193  
194 @@ -1519,7 +1529,7 @@ int cfg_parse_listen(const char *file, i
195                                 continue;
196                         }
197  
198 -                       Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n",
199 +                       Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'accept-proxy', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n",
200                               file, linenum, args[0]);
201                         err_code |= ERR_ALERT | ERR_FATAL;
202                         goto out;
203 @@ -5726,6 +5736,9 @@ out_uri_auth_compat:
204                         listener->handler = process_session;
205                         listener->analysers |= curproxy->fe_req_ana;
206  
207 +                       if (listener->options & LI_O_ACC_PROXY)
208 +                               listener->analysers |= AN_REQ_DECODE_PROXY;
209 +
210                         /* smart accept mode is automatic in HTTP mode */
211                         if ((curproxy->options2 & PR_O2_SMARTACC) ||
212                             (curproxy->mode == PR_MODE_HTTP &&
213 --- a/src/client.c
214 +++ b/src/client.c
215 @@ -22,6 +22,7 @@
216  
217  #include <common/compat.h>
218  #include <common/config.h>
219 +#include <common/debug.h>
220  #include <common/time.h>
221  
222  #include <types/global.h>
223 @@ -43,6 +44,191 @@
224  #include <proto/task.h>
225  
226  
227 +/* This analyser tries to fetch a line from the request buffer which looks like :
228 + *
229 + *   "PROXY" <SP> PROTO <SP> SRC3 <SP> DST3 <SP> SRC4 <SP> <DST4> "\r\n"
230 + *
231 + * There must be exactly one space between each field. Fields are :
232 + *  - PROTO : layer 4 protocol, which must be "TCP4" or "TCP6".
233 + *  - SRC3  : layer 3 (eg: IP) source address in standard text form
234 + *  - DST3  : layer 3 (eg: IP) destination address in standard text form
235 + *  - SRC4  : layer 4 (eg: TCP port) source address in standard text form
236 + *  - DST4  : layer 4 (eg: TCP port) destination address in standard text form
237 + *
238 + * This line MUST be at the beginning of the buffer and MUST NOT wrap.
239 + *
240 + * Once the data is fetched, the values are set in the session's field and data
241 + * are removed from the buffer. The function returns zero if it needs to wait
242 + * for more data (max: timeout_client), or 1 if it has finished and removed itself.
243 + */
244 +int frontend_decode_proxy_request(struct session *s, struct buffer *req, int an_bit)
245 +{
246 +       char *line = req->data;
247 +       char *end = req->data + req->l;
248 +       int len;
249 +
250 +       DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
251 +               now_ms, __FUNCTION__,
252 +               s,
253 +               req,
254 +               req->rex, req->wex,
255 +               req->flags,
256 +               req->l,
257 +               req->analysers);
258 +
259 +       if (req->flags & (BF_READ_ERROR|BF_READ_TIMEOUT))
260 +               goto fail;
261 +
262 +       len = MIN(req->l, 6);
263 +       if (!len)
264 +               goto missing;
265 +
266 +       /* Decode a possible proxy request, fail early if it does not match */
267 +       if (strncmp(line, "PROXY ", len) != 0)
268 +               goto fail;
269 +
270 +       line += 6;
271 +       if (req->l < 18) /* shortest possible line */
272 +               goto missing;
273 +
274 +       if (!memcmp(line, "TCP4 ", 5) != 0) {
275 +               u32 src3, dst3, sport, dport;
276 +
277 +               line += 5;
278 +
279 +               src3 = inetaddr_host_lim_ret(line, end, &line);
280 +               if (line == end)
281 +                       goto missing;
282 +               if (*line++ != ' ')
283 +                       goto fail;
284 +
285 +               dst3 = inetaddr_host_lim_ret(line, end, &line);
286 +               if (line == end)
287 +                       goto missing;
288 +               if (*line++ != ' ')
289 +                       goto fail;
290 +
291 +               sport = read_uint((const char **)&line, end);
292 +               if (line == end)
293 +                       goto missing;
294 +               if (*line++ != ' ')
295 +                       goto fail;
296 +
297 +               dport = read_uint((const char **)&line, end);
298 +               if (line > end - 2)
299 +                       goto missing;
300 +               if (*line++ != '\r')
301 +                       goto fail;
302 +               if (*line++ != '\n')
303 +                       goto fail;
304 +
305 +               /* update the session's addresses and mark them set */
306 +               ((struct sockaddr_in *)&s->cli_addr)->sin_family      = AF_INET;
307 +               ((struct sockaddr_in *)&s->cli_addr)->sin_addr.s_addr = htonl(src3);
308 +               ((struct sockaddr_in *)&s->cli_addr)->sin_port        = htons(sport);
309 +
310 +               ((struct sockaddr_in *)&s->frt_addr)->sin_family      = AF_INET;
311 +               ((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr = htonl(dst3);
312 +               ((struct sockaddr_in *)&s->frt_addr)->sin_port        = htons(dport);
313 +               s->flags |= SN_FRT_ADDR_SET;
314 +
315 +       }
316 +       else if (!memcmp(line, "TCP6 ", 5) != 0) {
317 +               u32 sport, dport;
318 +               char *src_s;
319 +               char *dst_s, *sport_s, *dport_s;
320 +               struct in6_addr src3, dst3;
321 +
322 +               line+=5;
323 +
324 +               src_s = line;
325 +               dst_s = sport_s = dport_s = NULL;
326 +               while (1) {
327 +                       if (line > end - 2) {
328 +                               goto missing;
329 +                       }
330 +                       else if (*line == '\r') {
331 +                               *line = 0;
332 +                               line++;
333 +                               if (*line++ != '\n')
334 +                                       goto fail;
335 +                               break;
336 +                       }
337 +
338 +                       if (*line == ' ') {
339 +                               *line = 0;
340 +                               if (!dst_s)
341 +                                       dst_s = line+1;
342 +                               else if (!sport_s)
343 +                                       sport_s = line+1;
344 +                               else if (!dport_s)
345 +                                       dport_s = line+1;
346 +                       }
347 +                       line++;
348 +               }
349 +
350 +               if (!dst_s || !sport_s || !dport_s)
351 +                       goto fail;
352 +
353 +               sport = read_uint((const char **)&sport_s,dport_s-1);
354 +               if ( *sport_s != 0 )
355 +                       goto fail;
356 +
357 +               dport = read_uint((const char **)&dport_s,line-2);
358 +               if ( *dport_s != 0 )
359 +                       goto fail;
360 +
361 +               if (inet_pton(AF_INET6, src_s, (void *)&src3) != 1)
362 +                       goto fail;
363 +
364 +               if (inet_pton(AF_INET6, dst_s, (void *)&dst3) != 1)
365 +                       goto fail;
366 +
367 +               /* update the session's addresses and mark them set */
368 +               ((struct sockaddr_in6 *)&s->cli_addr)->sin6_family      = AF_INET6;
369 +               memcpy(&((struct sockaddr_in6 *)&s->cli_addr)->sin6_addr, &src3, sizeof(struct in6_addr));
370 +               ((struct sockaddr_in6 *)&s->cli_addr)->sin6_port        = htons(sport);
371 +
372 +               ((struct sockaddr_in6 *)&s->frt_addr)->sin6_family      = AF_INET6;
373 +               memcpy(&((struct sockaddr_in6 *)&s->frt_addr)->sin6_addr, &dst3, sizeof(struct in6_addr));
374 +               ((struct sockaddr_in6 *)&s->frt_addr)->sin6_port        = htons(dport);
375 +               s->flags |= SN_FRT_ADDR_SET;
376 +       }
377 +       else {
378 +               goto fail;
379 +       }
380 +
381 +       /* remove the PROXY line from the request */
382 +       len = line - req->data;
383 +       buffer_replace2(req, req->data, line, NULL, 0);
384 +       req->total -= len; /* don't count the header line */
385 +
386 +       req->analysers &= ~an_bit;
387 +       return 1;
388 +
389 + missing:
390 +       if (!(req->flags & (BF_SHUTR|BF_FULL))) {
391 +               buffer_dont_connect(s->req);
392 +               return 0;
393 +       }
394 +       /* missing data and buffer is either full or shutdown => fail */
395 +
396 + fail:
397 +       buffer_abort(req);
398 +       buffer_abort(s->rep);
399 +       req->analysers = 0;
400 +
401 +       s->fe->counters.failed_req++;
402 +       if (s->listener->counters)
403 +               s->listener->counters->failed_req++;
404 +
405 +       if (!(s->flags & SN_ERR_MASK))
406 +               s->flags |= SN_ERR_PRXCOND;
407 +       if (!(s->flags & SN_FINST_MASK))
408 +               s->flags |= SN_FINST_R;
409 +       return 0;
410 +}
411 +
412  /* Retrieves the original destination address used by the client, and sets the
413   * SN_FRT_ADDR_SET flag.
414   */
415 --- a/src/proto_http.c
416 +++ b/src/proto_http.c
417 @@ -4156,7 +4156,8 @@ void http_end_txn_clean_session(struct s
418         if (s->rep->lr >= s->rep->data + s->rep->size)
419                 s->rep->lr -= s->req->size;
420  
421 -       s->req->analysers |= s->fe->fe_req_ana;
422 +       s->req->analysers = s->fe->fe_req_ana;
423 +       s->req->analysers &= ~AN_REQ_DECODE_PROXY;
424         s->rep->analysers = 0;
425  
426         http_silent_debug(__LINE__, s);
427 @@ -7741,7 +7742,6 @@ void http_reset_txn(struct session *s)
428         http_init_txn(s);
429  
430         s->be = s->fe;
431 -       s->req->analysers = s->listener->analysers;
432         s->logs.logwait = s->fe->to_log;
433         s->srv = s->prev_srv = s->srv_conn = NULL;
434         /* re-init store persistence */
435 --- a/src/session.c
436 +++ b/src/session.c
437 @@ -34,6 +34,7 @@
438  #include <proto/proxy.h>
439  #include <proto/queue.h>
440  #include <proto/server.h>
441 +#include <proto/client.h>
442  #include <proto/stick_table.h>
443  #include <proto/stream_interface.h>
444  #include <proto/stream_sock.h>
445 @@ -1071,6 +1072,12 @@ resync_stream_interface:
446                         while (ana_list && max_loops--) {
447                                 /* Warning! ensure that analysers are always placed in ascending order! */
448  
449 +                               if (ana_list & AN_REQ_DECODE_PROXY) {
450 +                                       if (!frontend_decode_proxy_request(s, s->req, AN_REQ_DECODE_PROXY))
451 +                                               break;
452 +                                       UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_DECODE_PROXY);
453 +                               }
454 +
455                                 if (ana_list & AN_REQ_INSPECT) {
456                                         if (!tcp_inspect_request(s, s->req, AN_REQ_INSPECT))
457                                                 break;
458 --- a/src/standard.c
459 +++ b/src/standard.c
460 @@ -569,6 +569,11 @@ unsigned int strl2uic(const char *s, int
461         return __strl2uic(s, len);
462  }
463  
464 +unsigned int read_uint(const char **s, const char *end)
465 +{
466 +       return __read_uint(s, end);
467 +}
468 +
469  /* This one is 7 times faster than strtol() on athlon with checks.
470   * It returns the value of the number composed of all valid digits read,
471   * and can process negative numbers too.
472 @@ -993,12 +998,12 @@ unsigned int inetaddr_host_lim(const cha
473   * Idem except the pointer to first unparsed byte is returned into <ret> which
474   * must not be NULL.
475   */
476 -unsigned int inetaddr_host_lim_ret(const char *text, char *stop, const char **ret)
477 +unsigned int inetaddr_host_lim_ret(char *text, char *stop, char **ret)
478  {
479         const unsigned int ascii_zero = ('0' << 24) | ('0' << 16) | ('0' << 8) | '0';
480         register unsigned int dig100, dig10, dig1;
481         int s;
482 -       const char *p, *d;
483 +       char *p, *d;
484  
485         dig1 = dig10 = dig100 = ascii_zero;
486         s = 24;