blob: 3367e3884cd5301b992d6b03cdf62601fadd01e3 [file] [log] [blame]
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001/*
2 * HTTP extensions logic and helpers
3 *
4 * Copyright 2022 HAProxy Technologies
5 *
Aurelien DARRAGONea3f5872024-02-28 11:25:31 +01006 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +010015 *
Aurelien DARRAGONea3f5872024-02-28 11:25:31 +010016 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +010019 */
20
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +010021#include <haproxy/sample.h>
22#include <haproxy/http_htx.h>
23#include <haproxy/http_ext.h>
24#include <haproxy/chunk.h>
25#include <haproxy/stream.h>
26#include <haproxy/proxy.h>
27#include <haproxy/sc_strm.h>
28#include <haproxy/obj_type.h>
29#include <haproxy/cfgparse.h>
Aurelien DARRAGON82faad12022-12-29 18:32:19 +010030#include <haproxy/arg.h>
31#include <haproxy/initcall.h>
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +010032#include <haproxy/tools.h>
33
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +010034/*
35 * =========== ANALYZE ===========
36 * below are http process/ana helpers
37 */
38
39/* checks if <input> contains rfc7239 compliant port
40 * Returns 1 for success and 0 for failure
41 * if <port> is not NULL, it will be set to the extracted value contained
42 * in <input>
43 * <input> will be consumed accordingly (parsed/extracted characters are
44 * removed from <input>)
45 */
46static inline int http_7239_extract_port(struct ist *input, uint16_t *port)
47{
48 char *start = istptr(*input);
49 uint32_t port_cast = 0;
50 int it = 0;
51
52 /* strtol does not support non-null terminated str,
53 * we extract port ourselves
54 */
55 while (it < istlen(*input) &&
56 isdigit((unsigned char)start[it])) {
57 port_cast = (port_cast * 10) + (start[it] - '0');
58 if (port_cast > 65535)
59 return 0; /* invalid port */
60 it += 1;
61 }
62 if (!port_cast)
63 return 0; /* invalid port */
64 /* ok */
65 if (port)
66 *port = (uint16_t)port_cast;
67 *input = istadv(*input, it);
68 return 1;
69}
70
Aurelien DARRAGON39254ca2023-03-03 13:11:36 +010071/* check if char is a valid obfuscated identifier char
72 * (according to 7239 RFC)
73 * Returns non zero value for valid char
74 */
75static inline int http_7239_valid_obfsc(char c)
76{
77 return (isalnum((unsigned char)c) ||
78 (c == '.' || c == '-' || c == '_'));
79}
80
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +010081/* checks if <input> contains rfc7239 compliant obfuscated identifier
82 * Returns 1 for success and 0 for failure
83 * if <obfs> is not NULL, it will be set to the extracted value contained
84 * in <input>
85 * <input> will be consumed accordingly (parsed/extracted characters are
86 * removed from <input>)
87 */
88static inline int http_7239_extract_obfs(struct ist *input, struct ist *obfs)
89{
90 int it = 0;
91
92 if (obfs)
93 obfs->ptr = input->ptr;
94
95 while (it < istlen(*input) && istptr(*input)[it] != ';') {
96 if (!http_7239_valid_obfsc(istptr(*input)[it]))
97 break; /* end of obfs token */
98 it += 1;
99 }
100 if (obfs)
101 obfs->len = it;
102 *input = istadv(*input, it);
103 return !!it;
104}
105
106/* checks if <input> contains rfc7239 compliant IPV4 address
107 * Returns 1 for success and 0 for failure
108 * if <ip> is not NULL, it will be set to the extracted value contained
109 * in <input>
110 * <input> will be consumed accordingly (parsed/extracted characters are
111 * removed from <input>)
112 */
113static inline int http_7239_extract_ipv4(struct ist *input, struct in_addr *ip)
114{
115 char ip4[INET_ADDRSTRLEN];
116 unsigned char buf[sizeof(struct in_addr)];
Aurelien DARRAGON529c3ad2024-03-18 15:18:08 +0100117 void *dst = buf;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100118 int it = 0;
119
Aurelien DARRAGON529c3ad2024-03-18 15:18:08 +0100120 if (ip)
121 dst = ip;
122
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100123 /* extract ipv4 addr */
124 while (it < istlen(*input) && it < (sizeof(ip4) - 1)) {
125 if (!isdigit((unsigned char)istptr(*input)[it]) &&
126 istptr(*input)[it] != '.')
127 break; /* no more ip4 char */
128 ip4[it] = istptr(*input)[it];
129 it += 1;
130 }
131 ip4[it] = 0;
Aurelien DARRAGON529c3ad2024-03-18 15:18:08 +0100132 if (inet_pton(AF_INET, ip4, dst) != 1)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100133 return 0; /* invalid ip4 addr */
134 /* ok */
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100135 *input = istadv(*input, it);
136 return 1;
137}
138
139/* checks if <input> contains rfc7239 compliant IPV6 address
140 * assuming input.len >= 1 and first char is '['
141 * Returns 1 for success and 0 for failure
142 * if <ip> is not NULL, it will be set to the extracted value contained
143 * in <input>
144 * <input> will be consumed accordingly (parsed/extracted characters are
145 * removed from <input>)
146 */
147static inline int http_7239_extract_ipv6(struct ist *input, struct in6_addr *ip)
148{
149 char ip6[INET6_ADDRSTRLEN];
150 unsigned char buf[sizeof(struct in6_addr)];
Aurelien DARRAGON529c3ad2024-03-18 15:18:08 +0100151 void *dst = buf;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100152 int it = 0;
153
Aurelien DARRAGON529c3ad2024-03-18 15:18:08 +0100154 if (ip)
155 dst = ip;
156
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100157 *input = istnext(*input); /* skip '[' leading char */
158 /* extract ipv6 addr */
159 while (it < istlen(*input) &&
160 it < (sizeof(ip6) - 1)) {
161 if (!isalnum((unsigned char)istptr(*input)[it]) &&
162 istptr(*input)[it] != ':')
163 break; /* no more ip6 char */
164 ip6[it] = istptr(*input)[it];
165 it += 1;
166 }
167 ip6[it] = 0;
168 if ((istlen(*input)-it) < 1 || istptr(*input)[it] != ']')
169 return 0; /* missing ending "]" char */
170 it += 1;
Aurelien DARRAGON529c3ad2024-03-18 15:18:08 +0100171 if (inet_pton(AF_INET6, ip6, dst) != 1)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100172 return 0; /* invalid ip6 addr */
173 /* ok */
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100174 *input = istadv(*input, it);
175 return 1;
176}
177
178/* checks if <input> contains rfc7239 compliant host
179 * <quoted> is used to determine if the current input is being extracted
180 * from a quoted (non zero) or unquoted (zero) token, as the parsing rules
Ilya Shipitsin07be66d2023-04-01 12:26:42 +0200181 * differ whether the input is quoted or not according to the rfc.
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100182 * Returns 1 for success and 0 for failure
183 * if <host> is not NULL, it will be set to the extracted value contained
184 * in <input>
185 * <input> will be consumed accordingly (parsed/extracted characters are
186 * removed from <input>)
187 */
188static inline int http_7239_extract_host(struct ist *input, struct ist *host, int quoted)
189{
190 if (istlen(*input) < 1)
191 return 0; /* invalid input */
192
193 if (host)
194 host->ptr = input->ptr;
195
196 if (quoted && *istptr(*input) == '[') {
197 /* raw ipv6 address */
198 if (!http_7239_extract_ipv6(input, NULL))
199 return 0; /* invalid addr */
200 }
201 else {
202 /* ipv4 or dns */
203 while (istlen(*input)) {
204 if (!isalnum((unsigned char)*istptr(*input)) &&
205 *istptr(*input) != '.')
206 break; /* end of hostname token */
207 *input = istnext(*input);
208 }
209 }
210 if (istlen(*input) < 1 || *istptr(*input) != ':') {
211 goto out; /* no optional port provided */
212 }
213 if (!quoted)
214 return 0; /* not supported */
215 *input = istnext(*input); /* skip ':' */
216 /* validate port */
217 if (!http_7239_extract_port(input, NULL))
218 return 0; /* invalid port */
219 out:
220 if (host)
221 host->len = (input->ptr - host->ptr);
222 return 1;
223}
224
225/* checks if <input> contains rfc7239 compliant nodename
226 * <quoted> is used to determine if the current input is being extracted
227 * from a quoted (non zero) or unquoted (zero) token, as the parsing rules
Ilya Shipitsin07be66d2023-04-01 12:26:42 +0200228 * differ whether the input is quoted or not according to the rfc.
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100229 * Returns 1 for success and 0 for failure
230 * if <nodename> is not NULL, it will be set to the extracted value contained
231 * in <input>
232 * <input> will be consumed accordingly (parsed/extracted characters are
233 * removed from <input>)
234 */
235static inline int http_7239_extract_nodename(struct ist *input, struct forwarded_header_nodename *nodename, int quoted)
236{
237 if (istlen(*input) < 1)
238 return 0; /* invalid input */
239 if (*istptr(*input) == '_') {
240 struct ist *obfs = NULL;
241
242 /* obfuscated nodename */
243 *input = istnext(*input); /* skip '_' */
244 if (nodename) {
245 nodename->type = FORWARDED_HEADER_OBFS;
246 obfs = &nodename->obfs;
247 }
248 if (!http_7239_extract_obfs(input, obfs))
249 return 0; /* invalid obfs */
250 } else if (*istptr(*input) == 'u') {
251 /* "unknown" nodename? */
252 if (istlen(*input) < 7 ||
253 strncmp("unknown", istptr(*input), 7))
254 return 0; /* syntax error */
255 *input = istadv(*input, 7); /* skip "unknown" */
256 if (nodename)
257 nodename->type = FORWARDED_HEADER_UNK;
258 } else if (quoted && *istptr(*input) == '[') {
259 struct in6_addr *ip6 = NULL;
260
261 /* ipv6 address */
262 if (nodename) {
263 struct sockaddr_in6 *addr = (void *)&nodename->ip;
264
265 ip6 = &addr->sin6_addr;
266 addr->sin6_family = AF_INET6;
267 nodename->type = FORWARDED_HEADER_IP;
268 }
269 if (!http_7239_extract_ipv6(input, ip6))
270 return 0; /* invalid ip6 */
271 } else if (*istptr(*input)) {
272 struct in_addr *ip = NULL;
273
274 /* ipv4 address */
275 if (nodename) {
276 struct sockaddr_in *addr = (void *)&nodename->ip;
277
278 ip = &addr->sin_addr;
279 addr->sin_family = AF_INET;
280 nodename->type = FORWARDED_HEADER_IP;
281 }
282 if (!http_7239_extract_ipv4(input, ip))
283 return 0; /* invalid ip */
284 } else
285 return 0; /* unexpected char */
286
287 /* ok */
288 return 1;
289}
290
291/* checks if <input> contains rfc7239 compliant nodeport
292 * <quoted> is used to determine if the current input is being extracted
293 * from a quoted (non zero) or unquoted (zero) token, as the parsing rules
Ilya Shipitsin07be66d2023-04-01 12:26:42 +0200294 * differ whether the input is quoted or not according to the rfc.
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100295 * Returns 1 for success and 0 for failure
296 * if <nodeport> is not NULL, it will be set to the extracted value contained
297 * in <input>
298 * <input> will be consumed accordingly (parsed/extracted characters are
299 * removed from <input>)
300 */
301static inline int http_7239_extract_nodeport(struct ist *input, struct forwarded_header_nodeport *nodeport)
302{
303 if (*istptr(*input) == '_') {
304 struct ist *obfs = NULL;
305
306 /* obfuscated nodeport */
307 *input = istnext(*input); /* skip '_' */
308 if (nodeport) {
309 nodeport->type = FORWARDED_HEADER_OBFS;
310 obfs = &nodeport->obfs;
311 }
312 if (!http_7239_extract_obfs(input, obfs))
313 return 0; /* invalid obfs */
314 } else {
315 uint16_t *port = NULL;
316
317 /* normal port */
318 if (nodeport) {
319 nodeport->type = FORWARDED_HEADER_PORT;
320 port = &nodeport->port;
321 }
322 if (!http_7239_extract_port(input, port))
323 return 0; /* invalid port */
324 }
325 /* ok */
326 return 1;
327}
328
329/* checks if <input> contains rfc7239 compliant node (nodename:nodeport token)
330 * <quoted> is used to determine if the current input is being extracted
331 * from a quoted (non zero) or unquoted (zero) token, as the parsing rules
Ilya Shipitsin07be66d2023-04-01 12:26:42 +0200332 * differ whether the input is quoted or not according to the rfc.
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100333 * Returns 1 for success and 0 for failure
334 * if <node> is not NULL, it will be set to the extracted value contained
335 * in <input>
336 * <input> will be consumed accordingly (parsed/extracted characters are
337 * removed from <input>)
338 */
339static inline int http_7239_extract_node(struct ist *input, struct forwarded_header_node *node, int quoted)
340{
341 struct forwarded_header_nodename *nodename = NULL;
342 struct forwarded_header_nodeport *nodeport = NULL;
343
344 if (node) {
345 nodename = &node->nodename;
346 nodeport = &node->nodeport;
347 node->raw.ptr = input->ptr;
348 }
349 if (!http_7239_extract_nodename(input, nodename, quoted))
350 return 0; /* invalid nodename */
351 if (istlen(*input) < 1 || *istptr(*input) != ':') {
352 if (node)
353 node->nodeport.type = FORWARDED_HEADER_UNK;
354 goto out; /* no optional port provided */
355 }
356 if (!quoted)
357 return 0; /* not supported */
358 *input = istnext(*input);
359 if (!http_7239_extract_nodeport(input, nodeport))
360 return 0; /* invalid nodeport */
361 out:
362 /* ok */
363 if (node)
364 node->raw.len = input->ptr - node->raw.ptr;
365 return 1;
366}
367
368static inline int _forwarded_header_save_ctx(struct forwarded_header_ctx *ctx, int current_step, int required_steps)
369{
370 return (ctx && (current_step & required_steps));
371}
372
373static inline void _forwarded_header_quote_expected(struct ist *hdr, uint8_t *quoted)
374{
375 if (istlen(*hdr) > 0 && *istptr(*hdr) == '"') {
376 *quoted = 1;
377 /* node is quoted, we must find corresponding
378 * ending quote at the end of the token
379 */
380 *hdr = istnext(*hdr); /* skip quote */
381 }
382}
383
384/* checks if current header <hdr> is RFC 7239 compliant and can be "trusted".
385 * function will stop parsing as soon as every <required_steps> have
386 * been validated or error is encountered.
387 * Provide FORWARDED_HEADER_ALL for a full header validating spectrum.
388 * You may provide limited scope to perform quick searches on specific attributes
389 * If <ctx> is provided (not NULL), parsed attributes will be stored according to
390 * their types, allowing you to extract some useful information from the header.
391 * Returns 0 on failure and <validated_steps> bitfield on success.
392 */
393int http_validate_7239_header(struct ist hdr, int required_steps, struct forwarded_header_ctx *ctx)
394{
395 int validated_steps = 0;
396 int current_step = 0;
397 uint8_t first = 1;
398 uint8_t quoted = 0;
399
400 while (istlen(hdr) && (required_steps & ~validated_steps)) {
401 if (!first) {
402 if (*istptr(hdr) == ';')
403 hdr = istnext(hdr); /* skip ';' */
404 else
405 goto not_ok; /* unexpected char */
406 }
407 else
408 first = 0;
409
410 if (!(validated_steps & FORWARDED_HEADER_FOR) && istlen(hdr) > 4 &&
411 strncmp("for=", istptr(hdr), 4) == 0) {
412 struct forwarded_header_node *node = NULL;
413
414 /* for parameter */
415 current_step = FORWARDED_HEADER_FOR;
416 hdr = istadv(hdr, 4); /* skip "for=" */
417 _forwarded_header_quote_expected(&hdr, &quoted);
418 if (_forwarded_header_save_ctx(ctx, current_step, required_steps))
419 node = &ctx->nfor;
420 /* validate node */
421 if (!http_7239_extract_node(&hdr, node, quoted))
422 goto not_ok; /* invalid node */
423 }
424 else if (!(validated_steps & FORWARDED_HEADER_BY) && istlen(hdr) > 3 &&
425 strncmp("by=", istptr(hdr), 3) == 0) {
426 struct forwarded_header_node *node = NULL;
427
428 /* by parameter */
429 current_step = FORWARDED_HEADER_BY;
430 hdr = istadv(hdr, 3); /* skip "by=" */
431 _forwarded_header_quote_expected(&hdr, &quoted);
432 if (_forwarded_header_save_ctx(ctx, current_step, required_steps))
433 node = &ctx->nby;
434 /* validate node */
435 if (!http_7239_extract_node(&hdr, node, quoted))
436 goto not_ok; /* invalid node */
437 }
438 else if (!(validated_steps & FORWARDED_HEADER_HOST) && istlen(hdr) > 5 &&
439 strncmp("host=", istptr(hdr), 5) == 0) {
440 struct ist *host = NULL;
441
442 /* host parameter */
443 current_step = FORWARDED_HEADER_HOST;
444 hdr = istadv(hdr, 5); /* skip "host=" */
445 _forwarded_header_quote_expected(&hdr, &quoted);
446 if (_forwarded_header_save_ctx(ctx, current_step, required_steps))
447 host = &ctx->host;
448 /* validate host */
449 if (!http_7239_extract_host(&hdr, host, quoted))
450 goto not_ok; /* invalid host */
451 }
452 else if (!(validated_steps & FORWARDED_HEADER_PROTO) && istlen(hdr) > 6 &&
453 strncmp("proto=", istptr(hdr), 6) == 0) {
454 /* proto parameter */
455 current_step = FORWARDED_HEADER_PROTO;
456 hdr = istadv(hdr, 6); /* skip "proto=" */
457 /* validate proto (only common used http|https are supported for now) */
458 if (istlen(hdr) < 4 || strncmp("http", istptr(hdr), 4))
459 goto not_ok;
460 hdr = istadv(hdr, 4); /* skip "http" */
461 if (istlen(hdr) && *istptr(hdr) == 's') {
462 hdr = istnext(hdr);
463 if (_forwarded_header_save_ctx(ctx, current_step, required_steps))
464 ctx->proto = FORWARDED_HEADER_HTTPS;
465 } else if (_forwarded_header_save_ctx(ctx, current_step, required_steps))
466 ctx->proto = FORWARDED_HEADER_HTTP;
467 /* rfc allows for potential proto quoting, but we don't support
468 * it: it is not common usage
469 */
470 }
471 else {
472 /* not supported
473 * rfc allows for upcoming extensions
474 * but obviously, we can't trust them
475 * as they are not yet standardized
476 */
477
478 goto not_ok;
479 }
480 /* quote check */
481 if (quoted) {
482 if (istlen(hdr) < 1 || *istptr(hdr) != '"') {
483 /* matching ending quote not found */
484 goto not_ok;
485 }
486 hdr = istnext(hdr); /* skip ending quote */
487 quoted = 0; /* reset */
488 }
489 validated_steps |= current_step;
490 }
491
492 return validated_steps;
493
494 not_ok:
495 return 0;
496}
497
Aurelien DARRAGON8436c912023-01-30 09:28:57 +0100498static inline void _7239_print_ip6(struct buffer *out, struct in6_addr *ip6_addr, int quoted)
499{
500 char pn[INET6_ADDRSTRLEN];
501
502 inet_ntop(AF_INET6,
503 ip6_addr,
504 pn, sizeof(pn));
505 if (!quoted)
506 chunk_appendf(out, "\""); /* explicit quoting required for ipv6 */
507 chunk_appendf(out, "[%s]", pn);
508}
509
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100510static inline void http_build_7239_header_nodename(struct buffer *out,
511 struct stream *s, struct proxy *curproxy,
512 const struct sockaddr_storage *addr,
513 struct http_ext_7239_forby *forby)
514{
515 struct in6_addr *ip6_addr;
Aurelien DARRAGON8436c912023-01-30 09:28:57 +0100516 int quoted = !!forby->np_mode;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100517
518 if (forby->nn_mode == HTTP_7239_FORBY_ORIG) {
519 if (addr && addr->ss_family == AF_INET) {
520 unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)addr)->sin_addr;
521
522 chunk_appendf(out, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
523 }
524 else if (addr && addr->ss_family == AF_INET6) {
525 ip6_addr = &((struct sockaddr_in6 *)addr)->sin6_addr;
Aurelien DARRAGON8436c912023-01-30 09:28:57 +0100526 _7239_print_ip6(out, ip6_addr, quoted);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100527 }
528 /* else: not supported */
529 }
530 else if (forby->nn_mode == HTTP_7239_FORBY_SMP && forby->nn_expr) {
531 struct sample *smp;
532
533 smp = sample_process(curproxy, s->sess, s,
534 SMP_OPT_DIR_REQ | SMP_OPT_FINAL, forby->nn_expr, NULL);
535
536 if (smp) {
537 if (smp->data.type == SMP_T_IPV6) {
538 /* smp is valid IP6, print with RFC compliant output */
539 ip6_addr = &smp->data.u.ipv6;
Aurelien DARRAGON8436c912023-01-30 09:28:57 +0100540 _7239_print_ip6(out, ip6_addr, quoted);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100541 }
Aurelien DARRAGON8436c912023-01-30 09:28:57 +0100542 else if (sample_casts[smp->data.type][SMP_T_STR] &&
543 sample_casts[smp->data.type][SMP_T_STR](smp)) {
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100544 struct ist validate_n = ist2(smp->data.u.str.area, smp->data.u.str.data);
545 struct ist validate_o = ist2(smp->data.u.str.area, smp->data.u.str.data);
546 struct forwarded_header_nodename nodename;
547
548 /* validate nodename */
549 if (http_7239_extract_nodename(&validate_n, &nodename, 1) &&
550 !istlen(validate_n)) {
551 if (nodename.type == FORWARDED_HEADER_IP &&
552 nodename.ip.ss_family == AF_INET6) {
553 /* special care needed for valid ip6 nodename (quoting) */
554 ip6_addr = &((struct sockaddr_in6 *)&nodename.ip)->sin6_addr;
Aurelien DARRAGON8436c912023-01-30 09:28:57 +0100555 _7239_print_ip6(out, ip6_addr, quoted);
556 } else {
557 /* no special care needed, input is already rfc compliant,
558 * just print as regular non quoted string
559 */
560 chunk_cat(out, &smp->data.u.str);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100561 }
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100562 }
563 else if (http_7239_extract_obfs(&validate_o, NULL) &&
564 !istlen(validate_o)) {
565 /* raw user input that should be printed as 7239 obfs */
566 chunk_appendf(out, "_%.*s", (int)smp->data.u.str.data, smp->data.u.str.area);
567 }
568 /* else: not compliant */
569 }
570 /* else: cannot be casted to str */
571 }
572 /* else: smp error */
573 }
574}
575
576static inline void http_build_7239_header_nodeport(struct buffer *out,
577 struct stream *s, struct proxy *curproxy,
578 const struct sockaddr_storage *addr,
579 struct http_ext_7239_forby *forby)
580{
581 if (forby->np_mode == HTTP_7239_FORBY_ORIG) {
582 if (addr && addr->ss_family == AF_INET)
583 chunk_appendf(out, "%d", ntohs(((struct sockaddr_in *)addr)->sin_port));
584 else if (addr && addr->ss_family == AF_INET6)
585 chunk_appendf(out, "%d", ntohs(((struct sockaddr_in6 *)addr)->sin6_port));
586 /* else: not supported */
587 }
588 else if (forby->np_mode == HTTP_7239_FORBY_SMP && forby->np_expr) {
589 struct sample *smp;
590
591 smp = sample_fetch_as_type(curproxy, s->sess, s,
592 SMP_OPT_DIR_REQ | SMP_OPT_FINAL, forby->np_expr, SMP_T_STR);
593 if (smp) {
594 struct ist validate_n = ist2(smp->data.u.str.area, smp->data.u.str.data);
595 struct ist validate_o = ist2(smp->data.u.str.area, smp->data.u.str.data);
596
597 /* validate nodeport */
598 if (http_7239_extract_nodeport(&validate_n, NULL) &&
599 !istlen(validate_n)) {
600 /* no special care needed, input is already rfc compliant,
601 * just print as regular non quoted string
602 */
603 chunk_cat(out, &smp->data.u.str);
604 }
605 else if (http_7239_extract_obfs(&validate_o, NULL) &&
606 !istlen(validate_o)) {
607 /* raw user input that should be printed as 7239 obfs */
608 chunk_appendf(out, "_%.*s", (int)smp->data.u.str.data, smp->data.u.str.area);
609 }
610 /* else: not compliant */
611 }
612 /* else: smp error */
613 }
614}
615
616static inline void http_build_7239_header_node(struct buffer *out,
617 struct stream *s, struct proxy *curproxy,
618 const struct sockaddr_storage *addr,
619 struct http_ext_7239_forby *forby)
620{
621 size_t offset_start;
622 size_t offset_save;
623
624 offset_start = out->data;
625 if (forby->np_mode)
626 chunk_appendf(out, "\"");
627 offset_save = out->data;
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100628 http_build_7239_header_nodename(out, s, curproxy, addr, forby);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100629 if (offset_save == out->data) {
630 /* could not build nodename, either because some
631 * data is not available or user is providing bad input
632 */
633 chunk_appendf(out, "unknown");
634 }
635 if (forby->np_mode) {
636 chunk_appendf(out, ":");
637 offset_save = out->data;
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100638 http_build_7239_header_nodeport(out, s, curproxy, addr, forby);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100639 if (offset_save == out->data) {
640 /* could not build nodeport, either because some data is
641 * not available or user is providing bad input
642 */
643 out->data = offset_save - 1;
644 }
645 }
646 if (out->data != offset_start && out->area[offset_start] == '"')
647 chunk_appendf(out, "\""); /* add matching end quote */
648}
649
650static inline void http_build_7239_header_host(struct buffer *out,
651 struct stream *s, struct proxy *curproxy,
652 struct htx *htx, struct http_ext_7239_host *host)
653{
654 struct http_hdr_ctx ctx = { .blk = NULL };
655 char *str = NULL;
656 int str_len = 0;
657
658 if (host->mode == HTTP_7239_HOST_ORIG &&
659 http_find_header(htx, ist("host"), &ctx, 0)) {
660 str = ctx.value.ptr;
661 str_len = ctx.value.len;
662 print_host:
663 {
664 struct ist validate = ist2(str, str_len);
665 /* host check, to ensure rfc compliant output
Ilya Shipitsin07be66d2023-04-01 12:26:42 +0200666 * (assuming host is quoted/escaped)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100667 */
668 if (http_7239_extract_host(&validate, NULL, 1) && !istlen(validate))
669 chunk_memcat(out, str, str_len);
670 /* else: not compliant or partially compliant */
671 }
672
673 }
674 else if (host->mode == HTTP_7239_HOST_SMP && host->expr) {
675 struct sample *smp;
676
677 smp = sample_fetch_as_type(curproxy, s->sess, s,
678 SMP_OPT_DIR_REQ | SMP_OPT_FINAL, host->expr, SMP_T_STR);
679 if (smp) {
680 str = smp->data.u.str.area;
681 str_len = smp->data.u.str.data;
682 goto print_host;
683 }
684 /* else: smp error */
685 }
686}
687
688/* Tries build 7239 header according to <curproxy> parameters and <s> context
689 * It both depends on <curproxy>->http_ext->fwd for config and <s> for request
690 * context data.
691 * The function will write output to <out> buffer
692 * Returns 1 for success and 0 for error (ie: not enough space in buffer)
693 */
694static int http_build_7239_header(struct buffer *out,
695 struct stream *s, struct proxy *curproxy, struct htx *htx)
696{
697 struct connection *cli_conn = objt_conn(strm_sess(s)->origin);
698
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100699 if (curproxy->http_ext->fwd->p_proto) {
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100700 chunk_appendf(out, "%sproto=%s", ((out->data) ? ";" : ""),
701 ((conn_is_ssl(cli_conn)) ? "https" : "http"));
702 }
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100703 if (curproxy->http_ext->fwd->p_host.mode) {
Ilya Shipitsin07be66d2023-04-01 12:26:42 +0200704 /* always add quotes for host parameter to make output compliance checks simpler */
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100705 chunk_appendf(out, "%shost=\"", ((out->data) ? ";" : ""));
706 /* ignore return value for now, but could be useful some day */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100707 http_build_7239_header_host(out, s, curproxy, htx, &curproxy->http_ext->fwd->p_host);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100708 chunk_appendf(out, "\"");
709 }
710
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100711 if (curproxy->http_ext->fwd->p_by.nn_mode) {
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100712 const struct sockaddr_storage *dst = sc_dst(s->scf);
713
714 chunk_appendf(out, "%sby=", ((out->data) ? ";" : ""));
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100715 http_build_7239_header_node(out, s, curproxy, dst, &curproxy->http_ext->fwd->p_by);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100716 }
717
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100718 if (curproxy->http_ext->fwd->p_for.nn_mode) {
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100719 const struct sockaddr_storage *src = sc_src(s->scf);
720
721 chunk_appendf(out, "%sfor=", ((out->data) ? ";" : ""));
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100722 http_build_7239_header_node(out, s, curproxy, src, &curproxy->http_ext->fwd->p_for);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100723 }
724 if (unlikely(out->data == out->size)) {
725 /* not enough space in buffer, error */
726 return 0;
727 }
728 return 1;
729}
730
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100731/* This function will try to inject RFC 7239 forwarded header if
732 * configured on the backend (ignored for frontends).
733 * Will do nothing if the option is not enabled on the proxy.
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100734 * Returns 1 for success and 0 for failure
735 */
736int http_handle_7239_header(struct stream *s, struct channel *req)
737{
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100738 struct proxy *curproxy = s->be; /* ignore frontend */
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100739
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100740 if (curproxy->http_ext && curproxy->http_ext->fwd) {
741 struct htx *htx = htxbuf(&req->buf);
742 int validate = 1;
743 struct http_hdr_ctx find = { .blk = NULL };
744 struct http_hdr_ctx last = { .blk = NULL};
745 struct ist hdr = ist("forwarded");
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100746
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100747 /* ok, let's build forwarded header */
748 chunk_reset(&trash);
749 if (unlikely(!http_build_7239_header(&trash, s, curproxy, htx)))
750 return 0; /* error when building header (bad user conf or memory error) */
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100751
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100752 /* validate existing forwarded header (including multiple values),
753 * hard stop if error is encountered
754 */
755 while (http_find_header(htx, hdr, &find, 0)) {
756 /* validate current header chunk */
757 if (!http_validate_7239_header(find.value, FORWARDED_HEADER_ALL, NULL)) {
758 /* at least one error, existing forwarded header not OK, add our own
759 * forwarded header, so that it can be trusted
760 */
761 validate = 0;
762 break;
763 }
764 last = find;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100765 }
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100766 /* no errors, append our data at the end of existing header */
767 if (last.blk && validate) {
768 if (unlikely(!http_append_header_value(htx, &last, ist2(trash.area, trash.data))))
769 return 0; /* htx error */
770 }
771 else {
772 if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
773 return 0; /* htx error */
774 }
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100775 }
776 return 1;
777}
778
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100779/*
780 * add X-Forwarded-For if either the frontend or the backend
781 * asks for it.
Aurelien DARRAGON730b9832022-12-28 18:53:05 +0100782 * Returns 1 for success and 0 for failure
783 */
784int http_handle_xff_header(struct stream *s, struct channel *req)
785{
786 struct session *sess = s->sess;
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100787 struct http_ext_xff *f_xff = NULL;
788 struct http_ext_xff *b_xff = NULL;
Aurelien DARRAGON730b9832022-12-28 18:53:05 +0100789
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100790 if (sess->fe->http_ext && sess->fe->http_ext->xff) {
791 /* frontend */
792 f_xff = sess->fe->http_ext->xff;
Aurelien DARRAGON730b9832022-12-28 18:53:05 +0100793 }
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100794 if (s->be->http_ext && s->be->http_ext->xff) {
795 /* backend */
796 b_xff = s->be->http_ext->xff;
797 }
798
799 if (f_xff || b_xff) {
800 struct htx *htx = htxbuf(&req->buf);
801 const struct sockaddr_storage *src = sc_src(s->scf);
802 struct http_hdr_ctx ctx = { .blk = NULL };
803 struct ist hdr = ((b_xff) ? b_xff->hdr_name : f_xff->hdr_name);
Aurelien DARRAGON730b9832022-12-28 18:53:05 +0100804
Aurelien DARRAGONddeba272023-06-20 14:55:11 +0200805 if ((!f_xff || f_xff->mode == HTTP_XFF_IFNONE) &&
806 (!b_xff || b_xff->mode == HTTP_XFF_IFNONE) &&
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100807 http_find_header(htx, hdr, &ctx, 0)) {
808 /* The header is set to be added only if none is present
809 * and we found it, so don't do anything.
Aurelien DARRAGON730b9832022-12-28 18:53:05 +0100810 */
Aurelien DARRAGON730b9832022-12-28 18:53:05 +0100811 }
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100812 else if (src && src->ss_family == AF_INET) {
813 /* Add an X-Forwarded-For header unless the source IP is
814 * in the 'except' network range.
815 */
816 if ((!f_xff || ipcmp2net(src, &f_xff->except_net)) &&
817 (!b_xff || ipcmp2net(src, &b_xff->except_net))) {
818 unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)src)->sin_addr;
Aurelien DARRAGON730b9832022-12-28 18:53:05 +0100819
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100820 /* Note: we rely on the backend to get the header name to be used for
821 * x-forwarded-for, because the header is really meant for the backends.
822 * However, if the backend did not specify any option, we have to rely
823 * on the frontend's header name.
824 */
825 chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
826 if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
827 return 0;
828 }
829 }
830 else if (src && src->ss_family == AF_INET6) {
831 /* Add an X-Forwarded-For header unless the source IP is
832 * in the 'except' network range.
Aurelien DARRAGON730b9832022-12-28 18:53:05 +0100833 */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100834 if ((!f_xff || ipcmp2net(src, &f_xff->except_net)) &&
835 (!b_xff || ipcmp2net(src, &b_xff->except_net))) {
836 char pn[INET6_ADDRSTRLEN];
837
838 inet_ntop(AF_INET6,
839 (const void *)&((struct sockaddr_in6 *)(src))->sin6_addr,
840 pn, sizeof(pn));
841
842 /* Note: we rely on the backend to get the header name to be used for
843 * x-forwarded-for, because the header is really meant for the backends.
844 * However, if the backend did not specify any option, we have to rely
845 * on the frontend's header name.
846 */
847 chunk_printf(&trash, "%s", pn);
848 if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
849 return 0;
850 }
Aurelien DARRAGON730b9832022-12-28 18:53:05 +0100851 }
852 }
Aurelien DARRAGON730b9832022-12-28 18:53:05 +0100853 return 1;
854}
855
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100856/*
857 * add X-Original-To if either the frontend or the backend
858 * asks for it.
Aurelien DARRAGONf9583412022-12-29 15:45:41 +0100859 * Returns 1 for success and 0 for failure
860 */
861int http_handle_xot_header(struct stream *s, struct channel *req)
862{
863 struct session *sess = s->sess;
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100864 struct http_ext_xot *f_xot = NULL;
865 struct http_ext_xot *b_xot = NULL;
Aurelien DARRAGONf9583412022-12-29 15:45:41 +0100866
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100867 if (sess->fe->http_ext && sess->fe->http_ext->xot) {
868 /* frontend */
869 f_xot = sess->fe->http_ext->xot;
870 }
871 if (s->be->http_ext && s->be->http_ext->xot) {
872 /* backend */
873 BUG_ON(!s->be->http_ext);
874 b_xot = s->be->http_ext->xot;
875 }
Aurelien DARRAGONf9583412022-12-29 15:45:41 +0100876
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100877 if (f_xot || b_xot) {
878 struct htx *htx = htxbuf(&req->buf);
879 const struct sockaddr_storage *dst = sc_dst(s->scf);
880 struct ist hdr = ((b_xot) ? b_xot->hdr_name : f_xot->hdr_name);
Aurelien DARRAGONf9583412022-12-29 15:45:41 +0100881
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100882 if (dst && dst->ss_family == AF_INET) {
883 /* Add an X-Original-To header unless the destination IP is
884 * in the 'except' network range.
Aurelien DARRAGONf9583412022-12-29 15:45:41 +0100885 */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100886 if ((!f_xot || ipcmp2net(dst, &f_xot->except_net)) &&
887 (!b_xot || ipcmp2net(dst, &b_xot->except_net))) {
888 unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)dst)->sin_addr;
889
890 /* Note: we rely on the backend to get the header name to be used for
891 * x-original-to, because the header is really meant for the backends.
892 * However, if the backend did not specify any option, we have to rely
893 * on the frontend's header name.
894 */
895 chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
896 if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
897 return 0;
898 }
Aurelien DARRAGONf9583412022-12-29 15:45:41 +0100899 }
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100900 else if (dst && dst->ss_family == AF_INET6) {
901 /* Add an X-Original-To header unless the source IP is
902 * in the 'except' network range.
903 */
904 if ((!f_xot || ipcmp2net(dst, &f_xot->except_net)) &&
905 (!b_xot || ipcmp2net(dst, &b_xot->except_net))) {
906 char pn[INET6_ADDRSTRLEN];
Aurelien DARRAGONf9583412022-12-29 15:45:41 +0100907
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100908 inet_ntop(AF_INET6,
909 (const void *)&((struct sockaddr_in6 *)dst)->sin6_addr,
910 pn, sizeof(pn));
Aurelien DARRAGONf9583412022-12-29 15:45:41 +0100911
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100912 /* Note: we rely on the backend to get the header name to be used for
913 * x-forwarded-for, because the header is really meant for the backends.
914 * However, if the backend did not specify any option, we have to rely
915 * on the frontend's header name.
916 */
917 chunk_printf(&trash, "%s", pn);
918 if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
919 return 0;
920 }
Aurelien DARRAGONf9583412022-12-29 15:45:41 +0100921 }
922 }
923 return 1;
924}
925
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100926/*
927 * =========== CONFIG ===========
928 * below are helpers to parse http ext options from the config
929 */
930static int proxy_http_parse_oom(const char *file, int linenum)
931{
932 int err_code = 0;
933
934 ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
935 err_code |= ERR_ALERT | ERR_ABORT;
936 return err_code;
937}
938
939static inline int _proxy_http_parse_7239_expr(char **args, int *cur_arg,
940 const char *file, int linenum,
941 char **expr_s)
942{
943 int err_code = 0;
944
945 if (!*args[*cur_arg + 1]) {
946 ha_alert("parsing [%s:%d]: '%s' expects <expr> as argument.\n",
947 file, linenum, args[*cur_arg]);
948 err_code |= ERR_ALERT | ERR_FATAL;
949 goto out;
950 }
951 *cur_arg += 1;
952 ha_free(expr_s);
953 *expr_s = strdup(args[*cur_arg]);
954 if (!*expr_s)
955 return proxy_http_parse_oom(file, linenum);
956 *cur_arg += 1;
957 out:
958 return err_code;
959}
960
Aurelien DARRAGON39254ca2023-03-03 13:11:36 +0100961/* forwarded/7239 RFC: tries to parse "option forwarded" config keyword
962 * Returns a composition of ERR_ABORT, ERR_ALERT, ERR_FATAL, ERR_WARN
963 */
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100964int proxy_http_parse_7239(char **args, int cur_arg,
965 struct proxy *curproxy, const struct proxy *defpx,
966 const char *file, int linenum)
967{
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100968 struct http_ext_7239 *fwd;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100969 int err_code = 0;
970
971 if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, "option forwarded", NULL)) {
972 /* option is ignored for frontends */
973 err_code |= ERR_WARN;
974 goto out;
975 }
976
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100977 if (!http_ext_7239_prepare(curproxy))
978 return proxy_http_parse_oom(file, linenum);
979
980 fwd = curproxy->http_ext->fwd;
981
982 fwd->p_proto = 0;
983 fwd->p_host.mode = 0;
984 fwd->p_for.nn_mode = 0;
985 fwd->p_for.np_mode = 0;
986 fwd->p_by.nn_mode = 0;
987 fwd->p_by.np_mode = 0;
988 ha_free(&fwd->c_file);
989 fwd->c_file = strdup(file);
990 fwd->c_line = linenum;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100991
992 /* start at 2, since 0+1 = "option" "forwarded" */
993 cur_arg = 2;
994 if (!*(args[cur_arg])) {
995 /* no optional argument provided, use default settings */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +0100996 fwd->p_for.nn_mode = HTTP_7239_FORBY_ORIG; /* enable for and mimic xff */
997 fwd->p_proto = 1; /* enable proto */
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +0100998 goto out;
999 }
1000 /* loop to go through optional arguments */
1001 while (*(args[cur_arg])) {
1002 if (strcmp(args[cur_arg], "proto") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001003 fwd->p_proto = 1;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001004 cur_arg += 1;
1005 } else if (strcmp(args[cur_arg], "host") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001006 fwd->p_host.mode = HTTP_7239_HOST_ORIG;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001007 cur_arg += 1;
1008 } else if (strcmp(args[cur_arg], "host-expr") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001009 fwd->p_host.mode = HTTP_7239_HOST_SMP;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001010 err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum,
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001011 &fwd->p_host.expr_s);
Aurelien DARRAGON4ecef9e2023-07-04 10:33:33 +02001012 if (err_code & ERR_CODE)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001013 goto out;
1014 } else if (strcmp(args[cur_arg], "by") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001015 fwd->p_by.nn_mode = HTTP_7239_FORBY_ORIG;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001016 cur_arg += 1;
1017 } else if (strcmp(args[cur_arg], "by-expr") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001018 fwd->p_by.nn_mode = HTTP_7239_FORBY_SMP;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001019 err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum,
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001020 &fwd->p_by.nn_expr_s);
Aurelien DARRAGON4ecef9e2023-07-04 10:33:33 +02001021 if (err_code & ERR_CODE)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001022 goto out;
1023 } else if (strcmp(args[cur_arg], "for") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001024 fwd->p_for.nn_mode = HTTP_7239_FORBY_ORIG;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001025 cur_arg += 1;
1026 } else if (strcmp(args[cur_arg], "for-expr") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001027 fwd->p_for.nn_mode = HTTP_7239_FORBY_SMP;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001028 err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum,
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001029 &fwd->p_for.nn_expr_s);
Aurelien DARRAGON4ecef9e2023-07-04 10:33:33 +02001030 if (err_code & ERR_CODE)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001031 goto out;
1032 } else if (strcmp(args[cur_arg], "by_port") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001033 fwd->p_by.np_mode = HTTP_7239_FORBY_ORIG;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001034 cur_arg += 1;
1035 } else if (strcmp(args[cur_arg], "by_port-expr") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001036 fwd->p_by.np_mode = HTTP_7239_FORBY_SMP;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001037 err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum,
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001038 &fwd->p_by.np_expr_s);
Aurelien DARRAGON4ecef9e2023-07-04 10:33:33 +02001039 if (err_code & ERR_CODE)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001040 goto out;
1041 } else if (strcmp(args[cur_arg], "for_port") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001042 fwd->p_for.np_mode = HTTP_7239_FORBY_ORIG;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001043 cur_arg += 1;
1044 } else if (strcmp(args[cur_arg], "for_port-expr") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001045 fwd->p_for.np_mode = HTTP_7239_FORBY_SMP;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001046 err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum,
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001047 &fwd->p_for.np_expr_s);
Aurelien DARRAGON4ecef9e2023-07-04 10:33:33 +02001048 if (err_code & ERR_CODE)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001049 goto out;
1050 } else {
1051 /* unknown suboption - catchall */
1052 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'proto', 'host', "
1053 "'host-expr', 'by', 'by-expr', 'by_port', 'by_port-expr', "
1054 "'for', 'for-expr', 'for_port' and 'for_port-expr'.\n",
1055 file, linenum, args[0], args[1]);
1056 err_code |= ERR_ALERT | ERR_FATAL;
1057 goto out;
1058 }
1059 } /* end while loop */
1060
1061 /* consistency check */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001062 if (fwd->p_by.np_mode &&
1063 !fwd->p_by.nn_mode) {
1064 fwd->p_by.np_mode = 0;
1065 ha_free(&fwd->p_by.np_expr_s);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001066 ha_warning("parsing [%s:%d] : '%s %s' : '%s' will be ignored because both 'by' "
1067 "and 'by-expr' are unset\n",
1068 file, linenum, args[0], args[1],
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001069 ((fwd->p_by.np_mode == HTTP_7239_FORBY_ORIG) ? "by_port" : "by_port-expr"));
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001070 err_code |= ERR_WARN;
1071 }
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001072 if (fwd->p_for.np_mode &&
1073 !fwd->p_for.nn_mode) {
1074 fwd->p_for.np_mode = 0;
1075 ha_free(&fwd->p_for.np_expr_s);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001076 ha_warning("parsing [%s:%d] : '%s %s' : '%s' will be ignored because both 'for' "
1077 "and 'for-expr' are unset\n",
1078 file, linenum, args[0], args[1],
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001079 ((fwd->p_for.np_mode == HTTP_7239_FORBY_ORIG) ? "for_port" : "for_port-expr"));
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001080 err_code |= ERR_WARN;
1081 }
1082
1083 out:
1084 return err_code;
1085}
1086
Aurelien DARRAGONc07001b2023-01-24 17:55:09 +01001087/* rfc7239 forwarded option needs a postparsing step
1088 * to convert parsing hints into runtime usable sample expressions
1089 * Returns a composition of ERR_NONE, ERR_FATAL, ERR_ALERT, ERR_WARN
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001090 */
1091int proxy_http_compile_7239(struct proxy *curproxy)
1092{
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001093 struct http_ext_7239 *fwd;
Aurelien DARRAGONc07001b2023-01-24 17:55:09 +01001094 int err = ERR_NONE;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001095 int loop;
1096
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001097 if (!(curproxy->cap & PR_CAP_BE)) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001098 /* no backend cap: not supported (ie: frontend) */
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001099 goto out;
1100 }
1101
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001102 /* should not happen (test should be performed after BE cap test) */
1103 BUG_ON(!curproxy->http_ext || !curproxy->http_ext->fwd);
1104
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001105 curproxy->conf.args.ctx = ARGC_OPT; /* option */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001106 curproxy->conf.args.file = curproxy->http_ext->fwd->c_file;
1107 curproxy->conf.args.line = curproxy->http_ext->fwd->c_line;
1108 fwd = curproxy->http_ext->fwd;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001109
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001110 /* it is important that we keep iterating on error to make sure
1111 * all fwd config fields are in the same state (post-parsing state)
1112 */
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001113 for (loop = 0; loop < 5; loop++) {
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001114 char **expr_str = NULL;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001115 struct sample_expr **expr = NULL;
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001116 struct sample_expr *cur_expr;
Aurelien DARRAGONc07001b2023-01-24 17:55:09 +01001117 char *err_str = NULL;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001118 int smp = 0;
1119 int idx = 0;
1120
1121 switch (loop) {
1122 case 0:
1123 /* host */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001124 expr_str = &fwd->p_host.expr_s;
1125 expr = &fwd->p_host.expr;
1126 smp = (fwd->p_host.mode == HTTP_7239_HOST_SMP);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001127 break;
1128 case 1:
1129 /* by->node */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001130 expr_str = &fwd->p_by.nn_expr_s;
1131 expr = &fwd->p_by.nn_expr;
1132 smp = (fwd->p_by.nn_mode == HTTP_7239_FORBY_SMP);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001133 break;
1134 case 2:
1135 /* by->nodeport */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001136 expr_str = &fwd->p_by.np_expr_s;
1137 expr = &fwd->p_by.np_expr;
1138 smp = (fwd->p_by.np_mode == HTTP_7239_FORBY_SMP);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001139 break;
1140 case 3:
1141 /* for->node */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001142 expr_str = &fwd->p_for.nn_expr_s;
1143 expr = &fwd->p_for.nn_expr;
1144 smp = (fwd->p_for.nn_mode == HTTP_7239_FORBY_SMP);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001145 break;
1146 case 4:
1147 /* for->nodeport */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001148 expr_str = &fwd->p_for.np_expr_s;
1149 expr = &fwd->p_for.np_expr;
1150 smp = (fwd->p_for.np_mode == HTTP_7239_FORBY_SMP);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001151 break;
1152 }
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001153 if (!smp)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001154 continue; /* no expr */
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001155
1156 /* expr and expr_str cannot be NULL past this point */
1157 BUG_ON(!expr || !expr_str);
1158
1159 if (!*expr_str) {
1160 /* should not happen unless system memory exhaustion */
1161 ha_alert("%s '%s' [%s:%d]: failed to parse 'option forwarded' expression : %s.\n",
1162 proxy_type_str(curproxy), curproxy->id,
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001163 fwd->c_file, fwd->c_line,
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001164 "memory error");
Aurelien DARRAGONc07001b2023-01-24 17:55:09 +01001165 err |= ERR_ALERT | ERR_FATAL;
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001166 continue;
1167 }
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001168
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001169 cur_expr =
1170 sample_parse_expr((char*[]){*expr_str, NULL}, &idx,
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001171 fwd->c_file,
1172 fwd->c_line,
Aurelien DARRAGONc07001b2023-01-24 17:55:09 +01001173 &err_str, &curproxy->conf.args, NULL);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001174
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001175 if (!cur_expr) {
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001176 ha_alert("%s '%s' [%s:%d]: failed to parse 'option forwarded' expression '%s' in : %s.\n",
1177 proxy_type_str(curproxy), curproxy->id,
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001178 fwd->c_file, fwd->c_line,
Aurelien DARRAGONc07001b2023-01-24 17:55:09 +01001179 *expr_str, err_str);
1180 ha_free(&err_str);
1181 err |= ERR_ALERT | ERR_FATAL;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001182 }
Aurelien DARRAGONd745a3f2023-01-06 15:06:49 +01001183 else if (!(cur_expr->fetch->val & SMP_VAL_BE_HRQ_HDR)) {
1184 /* fetch not available in this context: sample expr is resolved
1185 * within backend right after headers are processed.
1186 * (in http_process_request())
1187 * -> we simply warn the user about the misuse
1188 */
1189 ha_warning("%s '%s' [%s:%d]: in 'option forwarded' sample expression '%s' : "
1190 "some args extract information from '%s', "
1191 "none of which is available here.\n",
1192 proxy_type_str(curproxy), curproxy->id,
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001193 fwd->c_file, fwd->c_line,
Aurelien DARRAGONd745a3f2023-01-06 15:06:49 +01001194 *expr_str, sample_ckp_names(cur_expr->fetch->use));
Aurelien DARRAGONc07001b2023-01-24 17:55:09 +01001195 err |= ERR_WARN;
Aurelien DARRAGONd745a3f2023-01-06 15:06:49 +01001196 }
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001197 /* post parsing individual expr cleanup */
1198 ha_free(expr_str);
1199
1200 /* expr assignment */
1201 *expr = cur_expr;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001202 }
1203 curproxy->conf.args.file = NULL;
1204 curproxy->conf.args.line = 0;
1205
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001206 /* post parsing general cleanup */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001207 ha_free(&fwd->c_file);
1208 fwd->c_line = 0;
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001209
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001210 fwd->c_mode = 1; /* parsing completed */
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001211
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001212 out:
Aurelien DARRAGONc07001b2023-01-24 17:55:09 +01001213 return err;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001214}
1215
Aurelien DARRAGON39254ca2023-03-03 13:11:36 +01001216/* x-forwarded-for: tries to parse "option forwardfor" config keyword
1217 * Returns a composition of ERR_NONE, ERR_FATAL, ERR_ALERT
1218 */
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001219int proxy_http_parse_xff(char **args, int cur_arg,
1220 struct proxy *curproxy, const struct proxy *defpx,
1221 const char *file, int linenum)
1222{
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001223 struct http_ext_xff *xff;
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001224 int err_code = 0;
1225
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001226 if (!http_ext_xff_prepare(curproxy))
1227 return proxy_http_parse_oom(file, linenum);
1228
1229 xff = curproxy->http_ext->xff;
1230
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001231 /* insert x-forwarded-for field, but not for the IP address listed as an except.
1232 * set default options (ie: bitfield, header name, etc)
1233 */
1234
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001235 xff->mode = HTTP_XFF_ALWAYS;
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001236
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001237 istfree(&xff->hdr_name);
1238 xff->hdr_name = istdup(ist(DEF_XFORWARDFOR_HDR));
1239 if (!isttest(xff->hdr_name))
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001240 return proxy_http_parse_oom(file, linenum);
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001241 xff->except_net.family = AF_UNSPEC;
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001242
1243 /* loop to go through arguments - start at 2, since 0+1 = "option" "forwardfor" */
1244 cur_arg = 2;
1245 while (*(args[cur_arg])) {
1246 if (strcmp(args[cur_arg], "except") == 0) {
1247 unsigned char mask;
1248 int i;
1249
1250 /* suboption except - needs additional argument for it */
1251 if (*(args[cur_arg+1]) &&
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001252 str2net(args[cur_arg+1], 1, &xff->except_net.addr.v4.ip, &xff->except_net.addr.v4.mask)) {
1253 xff->except_net.family = AF_INET;
1254 xff->except_net.addr.v4.ip.s_addr &= xff->except_net.addr.v4.mask.s_addr;
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001255 }
1256 else if (*(args[cur_arg+1]) &&
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001257 str62net(args[cur_arg+1], &xff->except_net.addr.v6.ip, &mask)) {
1258 xff->except_net.family = AF_INET6;
1259 len2mask6(mask, &xff->except_net.addr.v6.mask);
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001260 for (i = 0; i < 16; i++)
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001261 xff->except_net.addr.v6.ip.s6_addr[i] &= xff->except_net.addr.v6.mask.s6_addr[i];
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001262 }
1263 else {
1264 ha_alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n",
1265 file, linenum, args[0], args[1], args[cur_arg]);
1266 err_code |= ERR_ALERT | ERR_FATAL;
1267 goto out;
1268 }
1269 /* flush useless bits */
1270 cur_arg += 2;
1271 } else if (strcmp(args[cur_arg], "header") == 0) {
1272 /* suboption header - needs additional argument for it */
1273 if (*(args[cur_arg+1]) == 0) {
1274 ha_alert("parsing [%s:%d] : '%s %s %s' expects <header_name> as argument.\n",
1275 file, linenum, args[0], args[1], args[cur_arg]);
1276 err_code |= ERR_ALERT | ERR_FATAL;
1277 goto out;
1278 }
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001279 istfree(&xff->hdr_name);
1280 xff->hdr_name = istdup(ist(args[cur_arg+1]));
1281 if (!isttest(xff->hdr_name))
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001282 return proxy_http_parse_oom(file, linenum);
1283 cur_arg += 2;
1284 } else if (strcmp(args[cur_arg], "if-none") == 0) {
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001285 xff->mode = HTTP_XFF_IFNONE;
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001286 cur_arg += 1;
1287 } else {
1288 /* unknown suboption - catchall */
1289 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'except', 'header' and 'if-none'.\n",
1290 file, linenum, args[0], args[1]);
1291 err_code |= ERR_ALERT | ERR_FATAL;
1292 goto out;
1293 }
1294 } /* end while loop */
1295 out:
1296 return err_code;
1297}
1298
Aurelien DARRAGON39254ca2023-03-03 13:11:36 +01001299/* x-original-to: tries to parse "option originalto" config keyword
1300 * Returns a composition of ERR_NONE, ERR_FATAL, ERR_ALERT
1301 */
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001302int proxy_http_parse_xot(char **args, int cur_arg,
1303 struct proxy *curproxy, const struct proxy *defpx,
1304 const char *file, int linenum)
1305{
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001306 struct http_ext_xot *xot;
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001307 int err_code = 0;
1308
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001309 if (!http_ext_xot_prepare(curproxy))
1310 return proxy_http_parse_oom(file, linenum);
1311
1312 xot = curproxy->http_ext->xot;
1313
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001314 /* insert x-original-to field, but not for the IP address listed as an except.
1315 * set default options (ie: bitfield, header name, etc)
1316 */
1317
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001318 istfree(&xot->hdr_name);
1319 xot->hdr_name = istdup(ist(DEF_XORIGINALTO_HDR));
1320 if (!isttest(xot->hdr_name))
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001321 return proxy_http_parse_oom(file, linenum);
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001322 xot->except_net.family = AF_UNSPEC;
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001323
1324 /* loop to go through arguments - start at 2, since 0+1 = "option" "originalto" */
1325 cur_arg = 2;
1326 while (*(args[cur_arg])) {
1327 if (strcmp(args[cur_arg], "except") == 0) {
1328 unsigned char mask;
1329 int i;
1330
1331 /* suboption except - needs additional argument for it */
1332 if (*(args[cur_arg+1]) &&
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001333 str2net(args[cur_arg+1], 1, &xot->except_net.addr.v4.ip, &xot->except_net.addr.v4.mask)) {
1334 xot->except_net.family = AF_INET;
1335 xot->except_net.addr.v4.ip.s_addr &= xot->except_net.addr.v4.mask.s_addr;
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001336 }
1337 else if (*(args[cur_arg+1]) &&
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001338 str62net(args[cur_arg+1], &xot->except_net.addr.v6.ip, &mask)) {
1339 xot->except_net.family = AF_INET6;
1340 len2mask6(mask, &xot->except_net.addr.v6.mask);
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001341 for (i = 0; i < 16; i++)
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001342 xot->except_net.addr.v6.ip.s6_addr[i] &= xot->except_net.addr.v6.mask.s6_addr[i];
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001343 }
1344 else {
1345 ha_alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n",
1346 file, linenum, args[0], args[1], args[cur_arg]);
1347 err_code |= ERR_ALERT | ERR_FATAL;
1348 goto out;
1349 }
1350 cur_arg += 2;
1351 } else if (strcmp(args[cur_arg], "header") == 0) {
1352 /* suboption header - needs additional argument for it */
1353 if (*(args[cur_arg+1]) == 0) {
1354 ha_alert("parsing [%s:%d] : '%s %s %s' expects <header_name> as argument.\n",
1355 file, linenum, args[0], args[1], args[cur_arg]);
1356 err_code |= ERR_ALERT | ERR_FATAL;
1357 goto out;
1358 }
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001359 istfree(&xot->hdr_name);
1360 xot->hdr_name = istdup(ist(args[cur_arg+1]));
1361 if (!isttest(xot->hdr_name))
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001362 return proxy_http_parse_oom(file, linenum);
1363 cur_arg += 2;
1364 } else {
1365 /* unknown suboption - catchall */
1366 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'except' and 'header'.\n",
1367 file, linenum, args[0], args[1]);
1368 err_code |= ERR_ALERT | ERR_FATAL;
1369 goto out;
1370 }
1371 } /* end while loop */
1372
1373 out:
1374 return err_code;
1375}
1376
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001377/*
1378 * =========== MGMT ===========
1379 * below are helpers to manage http ext options
1380 */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001381
1382/* Ensure http_ext->fwd is properly allocated and
1383 * initialized for <curproxy>.
1384 * The function will leverage http_ext_prepare() to make
1385 * sure http_ext is properly allocated and initialized as well.
1386 * Returns 1 for success and 0 for failure (memory error)
1387 */
1388int http_ext_7239_prepare(struct proxy *curproxy)
1389{
1390 struct http_ext_7239 *fwd;
1391
1392 if (!http_ext_prepare(curproxy))
1393 return 0;
1394 if (curproxy->http_ext->fwd)
1395 return 1; /* nothing to do */
1396
1397 fwd = malloc(sizeof(*fwd));
1398 if (!fwd)
1399 return 0;
1400 /* initialize fwd mandatory fields */
1401 fwd->c_mode = 0; /* pre-compile (parse) time */
1402 fwd->c_file = NULL;
1403 fwd->p_host.expr_s = NULL;
1404 fwd->p_by.nn_expr_s = NULL;
1405 fwd->p_by.np_expr_s = NULL;
1406 fwd->p_for.nn_expr_s = NULL;
1407 fwd->p_for.np_expr_s = NULL;
1408 /* assign */
1409 curproxy->http_ext->fwd = fwd;
1410 return 1;
1411}
1412
1413/* Ensure http_ext->xff is properly allocated and
1414 * initialized for <curproxy>.
1415 * The function will leverage http_ext_prepare() to make
1416 * sure http_ext is properly allocated and initialized as well.
1417 * Returns 1 for success and 0 for failure (memory error)
1418 */
1419int http_ext_xff_prepare(struct proxy *curproxy)
1420{
1421 struct http_ext_xff *xff;
1422
1423 if (!http_ext_prepare(curproxy))
1424 return 0;
1425 if (curproxy->http_ext->xff)
1426 return 1; /* nothing to do */
1427
1428 xff = malloc(sizeof(*xff));
1429 if (!xff)
1430 return 0;
1431 /* initialize xff mandatory fields */
1432 xff->hdr_name = IST_NULL;
1433 /* assign */
1434 curproxy->http_ext->xff = xff;
1435 return 1;
1436}
1437
1438/* Ensure http_ext->xot is properly allocated and
1439 * initialized for <curproxy>.
1440 * The function will leverage http_ext_prepare() to make
1441 * sure http_ext is properly allocated and initialized as well.
1442 * Returns 1 for success and 0 for failure (memory error)
1443 */
1444int http_ext_xot_prepare(struct proxy *curproxy)
1445{
1446 struct http_ext_xot *xot;
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001447
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001448 if (!http_ext_prepare(curproxy))
1449 return 0;
1450 if (curproxy->http_ext->xot)
1451 return 1; /* nothing to do */
1452
1453 xot = malloc(sizeof(*xot));
1454 if (!xot)
1455 return 0;
1456 /* initialize xot mandatory fields */
1457 xot->hdr_name = IST_NULL;
1458 /* assign */
1459 curproxy->http_ext->xot = xot;
1460 return 1;
1461}
1462
1463/* deep clean http_ext->fwd parameter for <curproxy>
1464 * http_ext->fwd will be freed
1465 * clean behavior will differ depending on http_ext->fwd
1466 * state. If fwd is in 'parsed' state, parsing hints will be
1467 * cleaned. Else, it means fwd is in 'compiled' state, in this
1468 * case we're cleaning compiled results.
1469 * This is because parse and compile memory areas are shared in
1470 * a single union to optimize struct http_ext_7239 size.
1471 */
1472void http_ext_7239_clean(struct proxy *curproxy)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001473{
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001474 struct http_ext_7239 *clean;
1475
1476 if (!curproxy->http_ext)
1477 return;
1478 clean = curproxy->http_ext->fwd;
1479 if (!clean)
1480 return; /* nothing to do */
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001481 if (!clean->c_mode) {
1482 /* parsed */
1483 ha_free(&clean->c_file);
1484 ha_free(&clean->p_host.expr_s);
1485 ha_free(&clean->p_by.nn_expr_s);
1486 ha_free(&clean->p_by.np_expr_s);
1487 ha_free(&clean->p_for.nn_expr_s);
1488 ha_free(&clean->p_for.np_expr_s);
1489 }
1490 else {
1491 /* compiled */
1492 release_sample_expr(clean->p_host.expr);
1493 clean->p_host.expr = NULL;
1494 release_sample_expr(clean->p_by.nn_expr);
1495 clean->p_by.nn_expr = NULL;
1496 release_sample_expr(clean->p_by.np_expr);
1497 clean->p_by.np_expr = NULL;
1498 release_sample_expr(clean->p_for.nn_expr);
1499 clean->p_for.nn_expr = NULL;
1500 release_sample_expr(clean->p_for.np_expr);
1501 clean->p_for.np_expr = NULL;
1502 }
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001503 /* free fwd */
1504 ha_free(&curproxy->http_ext->fwd);
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001505}
1506
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001507/* deep clean http_ext->xff parameter for <curproxy>
1508 * http_ext->xff will be freed
1509 */
1510void http_ext_xff_clean(struct proxy *curproxy)
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001511{
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001512 struct http_ext_xff *clean;
1513
1514 if (!curproxy->http_ext)
1515 return;
1516 clean = curproxy->http_ext->xff;
1517 if (!clean)
1518 return; /* nothing to do */
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001519 istfree(&clean->hdr_name);
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001520 /* free xff */
1521 ha_free(&curproxy->http_ext->xff);
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001522}
1523
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001524/* deep clean http_ext->xot parameter for <curproxy>
1525 * http_ext->xot will be freed
1526 */
1527void http_ext_xot_clean(struct proxy *curproxy)
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001528{
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001529 struct http_ext_xot *clean;
1530
1531 if (!curproxy->http_ext)
1532 return;
1533 clean = curproxy->http_ext->xot;
1534 if (!clean)
1535 return; /* nothing to do */
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001536 istfree(&clean->hdr_name);
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001537 /* free xot */
1538 ha_free(&curproxy->http_ext->xot);
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001539}
1540
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001541/* duplicate http_ext->fwd parameters from <def> to <cpy>
1542 * performs the required memory allocation and initialization
1543 */
1544void http_ext_7239_dup(const struct proxy *def, struct proxy *cpy)
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001545{
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001546 struct http_ext_7239 *dest = NULL;
1547 struct http_ext_7239 *orig = NULL;
1548
1549 /* feature requires backend cap */
1550 if (!(cpy->cap & PR_CAP_BE))
1551 return;
1552
1553 if (def->http_ext == NULL || def->http_ext->fwd == NULL)
1554 return;
1555
1556 orig = def->http_ext->fwd;
1557
Aurelien DARRAGON9ded8342023-01-06 12:16:28 +01001558 if (orig->c_mode)
1559 return; /* copy not supported once compiled */
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001560
1561 if (!http_ext_7239_prepare(cpy))
1562 return;
1563
1564 dest = cpy->http_ext->fwd;
1565
Aurelien DARRAGONb2bb9252022-12-28 15:37:57 +01001566 if (orig->c_file)
1567 dest->c_file = strdup(orig->c_file);
1568 dest->c_line = orig->c_line;
1569 /* proto */
1570 dest->p_proto = orig->p_proto;
1571 /* host */
1572 dest->p_host.mode = orig->p_host.mode;
1573 if (orig->p_host.expr_s)
1574 dest->p_host.expr_s = strdup(orig->p_host.expr_s);
1575 /* by - nodename */
1576 dest->p_by.nn_mode = orig->p_by.nn_mode;
1577 if (orig->p_by.nn_expr_s)
1578 dest->p_by.nn_expr_s = strdup(orig->p_by.nn_expr_s);
1579 /* by - nodeport */
1580 dest->p_by.np_mode = orig->p_by.np_mode;
1581 if (orig->p_by.np_expr_s)
1582 dest->p_by.np_expr_s = strdup(orig->p_by.np_expr_s);
1583 /* for - nodename */
1584 dest->p_for.nn_mode = orig->p_for.nn_mode;
1585 if (orig->p_for.nn_expr_s)
1586 dest->p_for.nn_expr_s = strdup(orig->p_for.nn_expr_s);
1587 /* for - nodeport */
1588 dest->p_for.np_mode = orig->p_for.np_mode;
1589 if (orig->p_for.np_expr_s)
1590 dest->p_for.np_expr_s = strdup(orig->p_for.np_expr_s);
1591}
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001592
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001593/* duplicate http_ext->xff parameters from <def> to <cpy>
1594 * performs the required memory allocation and initialization
1595 */
1596void http_ext_xff_dup(const struct proxy *def, struct proxy *cpy)
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001597{
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001598 struct http_ext_xff *dest = NULL;
1599 struct http_ext_xff *orig = NULL;
1600
1601 if (def->http_ext == NULL || def->http_ext->xff == NULL ||
1602 !http_ext_xff_prepare(cpy))
1603 return;
1604
1605 orig = def->http_ext->xff;
1606 dest = cpy->http_ext->xff;
1607
Aurelien DARRAGON730b9832022-12-28 18:53:05 +01001608 if (isttest(orig->hdr_name))
1609 dest->hdr_name = istdup(orig->hdr_name);
1610 dest->mode = orig->mode;
1611 dest->except_net = orig->except_net;
1612}
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001613
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001614/* duplicate http_ext->xot parameters from <def> to <cpy>
1615 * performs the required memory allocation and initialization
1616 */
1617void http_ext_xot_dup(const struct proxy *def, struct proxy *cpy)
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001618{
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001619 struct http_ext_xot *dest = NULL;
1620 struct http_ext_xot *orig = NULL;
1621
1622 if (def->http_ext == NULL || def->http_ext->xot == NULL ||
1623 !http_ext_xot_prepare(cpy))
1624 return;
1625
1626 orig = def->http_ext->xot;
1627 dest = cpy->http_ext->xot;
1628
Aurelien DARRAGONf9583412022-12-29 15:45:41 +01001629 if (isttest(orig->hdr_name))
1630 dest->hdr_name = istdup(orig->hdr_name);
1631 dest->except_net = orig->except_net;
1632}
Aurelien DARRAGON82faad12022-12-29 18:32:19 +01001633
Aurelien DARRAGONb2e2ec52023-01-09 11:09:03 +01001634/* Allocate new http_ext and initialize it
1635 * if needed
1636 * Returns 1 for success and 0 for failure
1637 */
1638int http_ext_prepare(struct proxy *curproxy)
1639{
1640 if (curproxy->http_ext)
1641 return 1; /* nothing to do */
1642
1643 curproxy->http_ext = malloc(sizeof(*curproxy->http_ext));
1644 if (!curproxy->http_ext)
1645 return 0; /* failure */
1646 /* first init, set supported ext to NULL */
1647 curproxy->http_ext->fwd = NULL;
1648 curproxy->http_ext->xff = NULL;
1649 curproxy->http_ext->xot = NULL;
1650 return 1;
1651}
1652
1653/* duplicate existing http_ext from <defproxy> to <curproxy>
1654 */
1655void http_ext_dup(const struct proxy *defproxy, struct proxy *curproxy)
1656{
1657 /* copy defproxy.http_ext members */
1658 http_ext_7239_dup(defproxy, curproxy);
1659 http_ext_xff_dup(defproxy, curproxy);
1660 http_ext_xot_dup(defproxy, curproxy);
1661}
1662
1663/* deep clean http_ext for <curproxy> (if previously allocated)
1664 */
1665void http_ext_clean(struct proxy *curproxy)
1666{
1667 if (!curproxy->http_ext)
1668 return; /* nothing to do */
1669 /* first, free supported ext */
1670 http_ext_7239_clean(curproxy);
1671 http_ext_xff_clean(curproxy);
1672 http_ext_xot_clean(curproxy);
1673
1674 /* then, free http_ext */
1675 ha_free(&curproxy->http_ext);
1676}
1677
1678/* soft clean (only clean http_ext if no more options are used) */
1679void http_ext_softclean(struct proxy *curproxy)
1680{
1681 if (!curproxy->http_ext)
1682 return; /* nothing to do */
1683 if (!curproxy->http_ext->fwd &&
1684 !curproxy->http_ext->xff &&
1685 !curproxy->http_ext->xot) {
1686 /* no more use for http_ext, all options are disabled */
1687 http_ext_clean(curproxy);
1688 }
1689}
1690
Ilya Shipitsin07be66d2023-04-01 12:26:42 +02001691/* Perform some consistency checks on px.http_ext after parsing
Aurelien DARRAGONc07001b2023-01-24 17:55:09 +01001692 * is completed.
1693 * We make sure to perform a softclean in case some options were
1694 * to be disabled in this check. This way we can release some memory.
1695 * Returns a composition of ERR_NONE, ERR_ALERT, ERR_FATAL, ERR_WARN
1696 */
1697static int check_http_ext_postconf(struct proxy *px) {
1698 int err = ERR_NONE;
1699
1700 if (px->http_ext) {
1701 /* consistency check for http_ext */
1702 if (px->mode != PR_MODE_HTTP && !(px->options & PR_O_HTTP_UPG)) {
1703 /* http is disabled on px, yet it is required by http_ext */
1704 if (px->http_ext->fwd) {
1705 ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n",
1706 "forwarded", proxy_type_str(px), px->id);
1707 err |= ERR_WARN;
1708 http_ext_7239_clean(px);
1709 }
1710 if (px->http_ext->xff) {
1711 ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n",
1712 "forwardfor", proxy_type_str(px), px->id);
1713 err |= ERR_WARN;
1714 http_ext_xff_clean(px);
1715 }
1716 if (px->http_ext->xot) {
1717 ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n",
1718 "originalto", proxy_type_str(px), px->id);
1719 err |= ERR_WARN;
1720 http_ext_xot_clean(px);
1721 }
1722 } else if (px->http_ext->fwd) {
1723 /* option "forwarded" may need to compile its expressions */
1724 err |= proxy_http_compile_7239(px);
1725 }
1726 /* http_ext post init early cleanup */
1727 http_ext_softclean(px);
1728
1729 }
1730 return err;
1731}
1732
1733REGISTER_POST_PROXY_CHECK(check_http_ext_postconf);
Aurelien DARRAGON82faad12022-12-29 18:32:19 +01001734/*
1735 * =========== CONV ===========
1736 * related converters
1737 */
1738
Aurelien DARRAGON5c6f86f2022-12-30 16:23:08 +01001739/* input: string representing 7239 forwarded header single value
1740 * does not take arguments
1741 * output: 1 if header is RFC compliant, 0 otherwise
1742 */
1743static int sample_conv_7239_valid(const struct arg *args, struct sample *smp, void *private)
1744{
1745 struct ist input = ist2(smp->data.u.str.area, smp->data.u.str.data);
1746
1747 smp->data.type = SMP_T_BOOL;
1748 smp->data.u.sint = !!http_validate_7239_header(input, FORWARDED_HEADER_ALL, NULL);
1749 return 1;
1750}
1751
Aurelien DARRAGON6fb58b82022-12-30 16:37:03 +01001752/* input: string representing 7239 forwarded header single value
1753 * argument: parameter name to look for in the header
1754 * output: header parameter raw value, as a string
1755 */
1756static int sample_conv_7239_field(const struct arg *args, struct sample *smp, void *private)
1757{
1758 struct ist input = ist2(smp->data.u.str.area, smp->data.u.str.data);
1759 struct buffer *output;
1760 struct forwarded_header_ctx ctx;
1761 int validate;
1762 int field = 0;
1763
1764 if (strcmp(args->data.str.area, "proto") == 0)
1765 field = FORWARDED_HEADER_PROTO;
1766 else if (strcmp(args->data.str.area, "host") == 0)
1767 field = FORWARDED_HEADER_HOST;
1768 else if (strcmp(args->data.str.area, "for") == 0)
1769 field = FORWARDED_HEADER_FOR;
1770 else if (strcmp(args->data.str.area, "by") == 0)
1771 field = FORWARDED_HEADER_BY;
1772
1773 validate = http_validate_7239_header(input, FORWARDED_HEADER_ALL, &ctx);
1774 if (!(validate & field))
1775 return 0; /* invalid header or header does not contain field */
1776 output = get_trash_chunk();
1777 switch (field) {
1778 case FORWARDED_HEADER_PROTO:
1779 if (ctx.proto == FORWARDED_HEADER_HTTP)
1780 chunk_appendf(output, "http");
1781 else if (ctx.proto == FORWARDED_HEADER_HTTPS)
1782 chunk_appendf(output, "https");
1783 break;
1784 case FORWARDED_HEADER_HOST:
1785 chunk_istcat(output, ctx.host);
1786 break;
1787 case FORWARDED_HEADER_FOR:
1788 chunk_istcat(output, ctx.nfor.raw);
1789 break;
1790 case FORWARDED_HEADER_BY:
1791 chunk_istcat(output, ctx.nby.raw);
1792 break;
1793 default:
1794 break;
1795 }
1796 smp->flags &= ~SMP_F_CONST;
1797 smp->data.type = SMP_T_STR;
1798 smp->data.u.str = *output;
1799 return 1;
1800}
1801
Aurelien DARRAGON07d67532022-12-30 16:45:42 +01001802/* input: substring representing 7239 forwarded header node
1803 * output: forwarded header nodename translated to either
1804 * ipv4 address, ipv6 address or str
1805 * ('_' prefix if obfuscated, or "unknown" if unknown)
1806 */
1807static int sample_conv_7239_n2nn(const struct arg *args, struct sample *smp, void *private)
1808{
1809 struct ist input = ist2(smp->data.u.str.area, smp->data.u.str.data);
1810 struct forwarded_header_node ctx;
1811 struct buffer *output;
1812
1813 if (http_7239_extract_node(&input, &ctx, 1) == 0)
1814 return 0; /* could not extract node */
1815 switch (ctx.nodename.type) {
1816 case FORWARDED_HEADER_UNK:
1817 output = get_trash_chunk();
1818 chunk_appendf(output, "unknown");
1819 smp->flags &= ~SMP_F_CONST;
1820 smp->data.type = SMP_T_STR;
1821 smp->data.u.str = *output;
1822 break;
1823 case FORWARDED_HEADER_OBFS:
1824 output = get_trash_chunk();
1825 chunk_appendf(output, "_"); /* append obfs prefix */
1826 chunk_istcat(output, ctx.nodename.obfs);
1827 smp->flags &= ~SMP_F_CONST;
1828 smp->data.type = SMP_T_STR;
1829 smp->data.u.str = *output;
1830 break;
1831 case FORWARDED_HEADER_IP:
1832 if (ctx.nodename.ip.ss_family == AF_INET) {
1833 smp->data.type = SMP_T_IPV4;
1834 smp->data.u.ipv4 = ((struct sockaddr_in *)&ctx.nodename.ip)->sin_addr;
1835 }
1836 else if (ctx.nodename.ip.ss_family == AF_INET6) {
1837 smp->data.type = SMP_T_IPV6;
1838 smp->data.u.ipv6 = ((struct sockaddr_in6 *)&ctx.nodename.ip)->sin6_addr;
1839 }
1840 else
1841 return 0; /* unsupported */
1842 break;
1843 default:
1844 return 0; /* unsupported */
1845 }
1846 return 1;
1847}
1848
Aurelien DARRAGON9a273b42022-12-30 16:56:08 +01001849/* input: substring representing 7239 forwarded header node
1850 * output: forwarded header nodeport translated to either
1851 * integer or str for obfuscated ('_' prefix)
1852 */
1853static int sample_conv_7239_n2np(const struct arg *args, struct sample *smp, void *private)
1854{
1855 struct ist input = ist2(smp->data.u.str.area, smp->data.u.str.data);
1856 struct forwarded_header_node ctx;
1857 struct buffer *output;
1858
1859 if (http_7239_extract_node(&input, &ctx, 1) == 0)
1860 return 0; /* could not extract node */
1861
1862 switch (ctx.nodeport.type) {
1863 case FORWARDED_HEADER_UNK:
1864 return 0; /* not provided */
1865 case FORWARDED_HEADER_OBFS:
1866 output = get_trash_chunk();
1867 chunk_appendf(output, "_"); /* append obfs prefix */
1868 chunk_istcat(output, ctx.nodeport.obfs);
1869 smp->flags &= ~SMP_F_CONST;
1870 smp->data.type = SMP_T_STR;
1871 smp->data.u.str = *output;
1872 break;
1873 case FORWARDED_HEADER_PORT:
1874 smp->data.type = SMP_T_SINT;
1875 smp->data.u.sint = ctx.nodeport.port;
1876 break;
1877 default:
1878 return 0; /* unsupported */
1879 }
1880
1881 return 1;
1882}
1883
Aurelien DARRAGON82faad12022-12-29 18:32:19 +01001884/* Note: must not be declared <const> as its list will be overwritten */
1885static struct sample_conv_kw_list sample_conv_kws = {ILH, {
Aurelien DARRAGON5c6f86f2022-12-30 16:23:08 +01001886 { "rfc7239_is_valid", sample_conv_7239_valid, 0, NULL, SMP_T_STR, SMP_T_BOOL},
Aurelien DARRAGON6fb58b82022-12-30 16:37:03 +01001887 { "rfc7239_field", sample_conv_7239_field, ARG1(1,STR), NULL, SMP_T_STR, SMP_T_STR},
Aurelien DARRAGON07d67532022-12-30 16:45:42 +01001888 { "rfc7239_n2nn", sample_conv_7239_n2nn, 0, NULL, SMP_T_STR, SMP_T_ANY},
Aurelien DARRAGON9a273b42022-12-30 16:56:08 +01001889 { "rfc7239_n2np", sample_conv_7239_n2np, 0, NULL, SMP_T_STR, SMP_T_ANY},
Aurelien DARRAGON82faad12022-12-29 18:32:19 +01001890 { NULL, NULL, 0, 0, 0 },
1891}};
1892
1893INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);