Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1 | /* |
| 2 | * HTTP extensions logic and helpers |
| 3 | * |
| 4 | * Copyright 2022 HAProxy Technologies |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public License |
| 8 | * as published by the Free Software Foundation; either version |
| 9 | * 2.1 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | */ |
| 12 | |
| 13 | /* forwarded header (7239 RFC) */ |
| 14 | |
| 15 | #include <haproxy/sample.h> |
| 16 | #include <haproxy/http_htx.h> |
| 17 | #include <haproxy/http_ext.h> |
| 18 | #include <haproxy/chunk.h> |
| 19 | #include <haproxy/stream.h> |
| 20 | #include <haproxy/proxy.h> |
| 21 | #include <haproxy/sc_strm.h> |
| 22 | #include <haproxy/obj_type.h> |
| 23 | #include <haproxy/cfgparse.h> |
Aurelien DARRAGON | 82faad1 | 2022-12-29 18:32:19 +0100 | [diff] [blame] | 24 | #include <haproxy/arg.h> |
| 25 | #include <haproxy/initcall.h> |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 26 | #include <haproxy/tools.h> |
| 27 | |
| 28 | /* check if char is a valid obfuscated identifier char |
| 29 | * (according to 7239 RFC) |
| 30 | * Returns non zero value for valid char |
| 31 | */ |
| 32 | static int http_7239_valid_obfsc(char c) |
| 33 | { |
| 34 | return (isalnum((unsigned char)c) || |
| 35 | (c == '.' || c == '-' || c == '_')); |
| 36 | } |
| 37 | |
| 38 | /* |
| 39 | * =========== ANALYZE =========== |
| 40 | * below are http process/ana helpers |
| 41 | */ |
| 42 | |
| 43 | /* checks if <input> contains rfc7239 compliant port |
| 44 | * Returns 1 for success and 0 for failure |
| 45 | * if <port> is not NULL, it will be set to the extracted value contained |
| 46 | * in <input> |
| 47 | * <input> will be consumed accordingly (parsed/extracted characters are |
| 48 | * removed from <input>) |
| 49 | */ |
| 50 | static inline int http_7239_extract_port(struct ist *input, uint16_t *port) |
| 51 | { |
| 52 | char *start = istptr(*input); |
| 53 | uint32_t port_cast = 0; |
| 54 | int it = 0; |
| 55 | |
| 56 | /* strtol does not support non-null terminated str, |
| 57 | * we extract port ourselves |
| 58 | */ |
| 59 | while (it < istlen(*input) && |
| 60 | isdigit((unsigned char)start[it])) { |
| 61 | port_cast = (port_cast * 10) + (start[it] - '0'); |
| 62 | if (port_cast > 65535) |
| 63 | return 0; /* invalid port */ |
| 64 | it += 1; |
| 65 | } |
| 66 | if (!port_cast) |
| 67 | return 0; /* invalid port */ |
| 68 | /* ok */ |
| 69 | if (port) |
| 70 | *port = (uint16_t)port_cast; |
| 71 | *input = istadv(*input, it); |
| 72 | return 1; |
| 73 | } |
| 74 | |
| 75 | /* checks if <input> contains rfc7239 compliant obfuscated identifier |
| 76 | * Returns 1 for success and 0 for failure |
| 77 | * if <obfs> is not NULL, it will be set to the extracted value contained |
| 78 | * in <input> |
| 79 | * <input> will be consumed accordingly (parsed/extracted characters are |
| 80 | * removed from <input>) |
| 81 | */ |
| 82 | static inline int http_7239_extract_obfs(struct ist *input, struct ist *obfs) |
| 83 | { |
| 84 | int it = 0; |
| 85 | |
| 86 | if (obfs) |
| 87 | obfs->ptr = input->ptr; |
| 88 | |
| 89 | while (it < istlen(*input) && istptr(*input)[it] != ';') { |
| 90 | if (!http_7239_valid_obfsc(istptr(*input)[it])) |
| 91 | break; /* end of obfs token */ |
| 92 | it += 1; |
| 93 | } |
| 94 | if (obfs) |
| 95 | obfs->len = it; |
| 96 | *input = istadv(*input, it); |
| 97 | return !!it; |
| 98 | } |
| 99 | |
| 100 | /* checks if <input> contains rfc7239 compliant IPV4 address |
| 101 | * Returns 1 for success and 0 for failure |
| 102 | * if <ip> is not NULL, it will be set to the extracted value contained |
| 103 | * in <input> |
| 104 | * <input> will be consumed accordingly (parsed/extracted characters are |
| 105 | * removed from <input>) |
| 106 | */ |
| 107 | static inline int http_7239_extract_ipv4(struct ist *input, struct in_addr *ip) |
| 108 | { |
| 109 | char ip4[INET_ADDRSTRLEN]; |
| 110 | unsigned char buf[sizeof(struct in_addr)]; |
| 111 | int it = 0; |
| 112 | |
| 113 | /* extract ipv4 addr */ |
| 114 | while (it < istlen(*input) && it < (sizeof(ip4) - 1)) { |
| 115 | if (!isdigit((unsigned char)istptr(*input)[it]) && |
| 116 | istptr(*input)[it] != '.') |
| 117 | break; /* no more ip4 char */ |
| 118 | ip4[it] = istptr(*input)[it]; |
| 119 | it += 1; |
| 120 | } |
| 121 | ip4[it] = 0; |
| 122 | if (inet_pton(AF_INET, ip4, buf) != 1) |
| 123 | return 0; /* invalid ip4 addr */ |
| 124 | /* ok */ |
| 125 | if (ip) |
| 126 | memcpy(ip, buf, sizeof(buf)); |
| 127 | *input = istadv(*input, it); |
| 128 | return 1; |
| 129 | } |
| 130 | |
| 131 | /* checks if <input> contains rfc7239 compliant IPV6 address |
| 132 | * assuming input.len >= 1 and first char is '[' |
| 133 | * Returns 1 for success and 0 for failure |
| 134 | * if <ip> is not NULL, it will be set to the extracted value contained |
| 135 | * in <input> |
| 136 | * <input> will be consumed accordingly (parsed/extracted characters are |
| 137 | * removed from <input>) |
| 138 | */ |
| 139 | static inline int http_7239_extract_ipv6(struct ist *input, struct in6_addr *ip) |
| 140 | { |
| 141 | char ip6[INET6_ADDRSTRLEN]; |
| 142 | unsigned char buf[sizeof(struct in6_addr)]; |
| 143 | int it = 0; |
| 144 | |
| 145 | *input = istnext(*input); /* skip '[' leading char */ |
| 146 | /* extract ipv6 addr */ |
| 147 | while (it < istlen(*input) && |
| 148 | it < (sizeof(ip6) - 1)) { |
| 149 | if (!isalnum((unsigned char)istptr(*input)[it]) && |
| 150 | istptr(*input)[it] != ':') |
| 151 | break; /* no more ip6 char */ |
| 152 | ip6[it] = istptr(*input)[it]; |
| 153 | it += 1; |
| 154 | } |
| 155 | ip6[it] = 0; |
| 156 | if ((istlen(*input)-it) < 1 || istptr(*input)[it] != ']') |
| 157 | return 0; /* missing ending "]" char */ |
| 158 | it += 1; |
| 159 | if (inet_pton(AF_INET6, ip6, buf) != 1) |
| 160 | return 0; /* invalid ip6 addr */ |
| 161 | /* ok */ |
| 162 | if (ip) |
| 163 | memcpy(ip, buf, sizeof(buf)); |
| 164 | *input = istadv(*input, it); |
| 165 | return 1; |
| 166 | } |
| 167 | |
| 168 | /* checks if <input> contains rfc7239 compliant host |
| 169 | * <quoted> is used to determine if the current input is being extracted |
| 170 | * from a quoted (non zero) or unquoted (zero) token, as the parsing rules |
| 171 | * differ wheteher the input is quoted or not according to the rfc. |
| 172 | * Returns 1 for success and 0 for failure |
| 173 | * if <host> is not NULL, it will be set to the extracted value contained |
| 174 | * in <input> |
| 175 | * <input> will be consumed accordingly (parsed/extracted characters are |
| 176 | * removed from <input>) |
| 177 | */ |
| 178 | static inline int http_7239_extract_host(struct ist *input, struct ist *host, int quoted) |
| 179 | { |
| 180 | if (istlen(*input) < 1) |
| 181 | return 0; /* invalid input */ |
| 182 | |
| 183 | if (host) |
| 184 | host->ptr = input->ptr; |
| 185 | |
| 186 | if (quoted && *istptr(*input) == '[') { |
| 187 | /* raw ipv6 address */ |
| 188 | if (!http_7239_extract_ipv6(input, NULL)) |
| 189 | return 0; /* invalid addr */ |
| 190 | } |
| 191 | else { |
| 192 | /* ipv4 or dns */ |
| 193 | while (istlen(*input)) { |
| 194 | if (!isalnum((unsigned char)*istptr(*input)) && |
| 195 | *istptr(*input) != '.') |
| 196 | break; /* end of hostname token */ |
| 197 | *input = istnext(*input); |
| 198 | } |
| 199 | } |
| 200 | if (istlen(*input) < 1 || *istptr(*input) != ':') { |
| 201 | goto out; /* no optional port provided */ |
| 202 | } |
| 203 | if (!quoted) |
| 204 | return 0; /* not supported */ |
| 205 | *input = istnext(*input); /* skip ':' */ |
| 206 | /* validate port */ |
| 207 | if (!http_7239_extract_port(input, NULL)) |
| 208 | return 0; /* invalid port */ |
| 209 | out: |
| 210 | if (host) |
| 211 | host->len = (input->ptr - host->ptr); |
| 212 | return 1; |
| 213 | } |
| 214 | |
| 215 | /* checks if <input> contains rfc7239 compliant nodename |
| 216 | * <quoted> is used to determine if the current input is being extracted |
| 217 | * from a quoted (non zero) or unquoted (zero) token, as the parsing rules |
| 218 | * differ wheteher the input is quoted or not according to the rfc. |
| 219 | * Returns 1 for success and 0 for failure |
| 220 | * if <nodename> is not NULL, it will be set to the extracted value contained |
| 221 | * in <input> |
| 222 | * <input> will be consumed accordingly (parsed/extracted characters are |
| 223 | * removed from <input>) |
| 224 | */ |
| 225 | static inline int http_7239_extract_nodename(struct ist *input, struct forwarded_header_nodename *nodename, int quoted) |
| 226 | { |
| 227 | if (istlen(*input) < 1) |
| 228 | return 0; /* invalid input */ |
| 229 | if (*istptr(*input) == '_') { |
| 230 | struct ist *obfs = NULL; |
| 231 | |
| 232 | /* obfuscated nodename */ |
| 233 | *input = istnext(*input); /* skip '_' */ |
| 234 | if (nodename) { |
| 235 | nodename->type = FORWARDED_HEADER_OBFS; |
| 236 | obfs = &nodename->obfs; |
| 237 | } |
| 238 | if (!http_7239_extract_obfs(input, obfs)) |
| 239 | return 0; /* invalid obfs */ |
| 240 | } else if (*istptr(*input) == 'u') { |
| 241 | /* "unknown" nodename? */ |
| 242 | if (istlen(*input) < 7 || |
| 243 | strncmp("unknown", istptr(*input), 7)) |
| 244 | return 0; /* syntax error */ |
| 245 | *input = istadv(*input, 7); /* skip "unknown" */ |
| 246 | if (nodename) |
| 247 | nodename->type = FORWARDED_HEADER_UNK; |
| 248 | } else if (quoted && *istptr(*input) == '[') { |
| 249 | struct in6_addr *ip6 = NULL; |
| 250 | |
| 251 | /* ipv6 address */ |
| 252 | if (nodename) { |
| 253 | struct sockaddr_in6 *addr = (void *)&nodename->ip; |
| 254 | |
| 255 | ip6 = &addr->sin6_addr; |
| 256 | addr->sin6_family = AF_INET6; |
| 257 | nodename->type = FORWARDED_HEADER_IP; |
| 258 | } |
| 259 | if (!http_7239_extract_ipv6(input, ip6)) |
| 260 | return 0; /* invalid ip6 */ |
| 261 | } else if (*istptr(*input)) { |
| 262 | struct in_addr *ip = NULL; |
| 263 | |
| 264 | /* ipv4 address */ |
| 265 | if (nodename) { |
| 266 | struct sockaddr_in *addr = (void *)&nodename->ip; |
| 267 | |
| 268 | ip = &addr->sin_addr; |
| 269 | addr->sin_family = AF_INET; |
| 270 | nodename->type = FORWARDED_HEADER_IP; |
| 271 | } |
| 272 | if (!http_7239_extract_ipv4(input, ip)) |
| 273 | return 0; /* invalid ip */ |
| 274 | } else |
| 275 | return 0; /* unexpected char */ |
| 276 | |
| 277 | /* ok */ |
| 278 | return 1; |
| 279 | } |
| 280 | |
| 281 | /* checks if <input> contains rfc7239 compliant nodeport |
| 282 | * <quoted> is used to determine if the current input is being extracted |
| 283 | * from a quoted (non zero) or unquoted (zero) token, as the parsing rules |
| 284 | * differ wheteher the input is quoted or not according to the rfc. |
| 285 | * Returns 1 for success and 0 for failure |
| 286 | * if <nodeport> is not NULL, it will be set to the extracted value contained |
| 287 | * in <input> |
| 288 | * <input> will be consumed accordingly (parsed/extracted characters are |
| 289 | * removed from <input>) |
| 290 | */ |
| 291 | static inline int http_7239_extract_nodeport(struct ist *input, struct forwarded_header_nodeport *nodeport) |
| 292 | { |
| 293 | if (*istptr(*input) == '_') { |
| 294 | struct ist *obfs = NULL; |
| 295 | |
| 296 | /* obfuscated nodeport */ |
| 297 | *input = istnext(*input); /* skip '_' */ |
| 298 | if (nodeport) { |
| 299 | nodeport->type = FORWARDED_HEADER_OBFS; |
| 300 | obfs = &nodeport->obfs; |
| 301 | } |
| 302 | if (!http_7239_extract_obfs(input, obfs)) |
| 303 | return 0; /* invalid obfs */ |
| 304 | } else { |
| 305 | uint16_t *port = NULL; |
| 306 | |
| 307 | /* normal port */ |
| 308 | if (nodeport) { |
| 309 | nodeport->type = FORWARDED_HEADER_PORT; |
| 310 | port = &nodeport->port; |
| 311 | } |
| 312 | if (!http_7239_extract_port(input, port)) |
| 313 | return 0; /* invalid port */ |
| 314 | } |
| 315 | /* ok */ |
| 316 | return 1; |
| 317 | } |
| 318 | |
| 319 | /* checks if <input> contains rfc7239 compliant node (nodename:nodeport token) |
| 320 | * <quoted> is used to determine if the current input is being extracted |
| 321 | * from a quoted (non zero) or unquoted (zero) token, as the parsing rules |
| 322 | * differ wheteher the input is quoted or not according to the rfc. |
| 323 | * Returns 1 for success and 0 for failure |
| 324 | * if <node> is not NULL, it will be set to the extracted value contained |
| 325 | * in <input> |
| 326 | * <input> will be consumed accordingly (parsed/extracted characters are |
| 327 | * removed from <input>) |
| 328 | */ |
| 329 | static inline int http_7239_extract_node(struct ist *input, struct forwarded_header_node *node, int quoted) |
| 330 | { |
| 331 | struct forwarded_header_nodename *nodename = NULL; |
| 332 | struct forwarded_header_nodeport *nodeport = NULL; |
| 333 | |
| 334 | if (node) { |
| 335 | nodename = &node->nodename; |
| 336 | nodeport = &node->nodeport; |
| 337 | node->raw.ptr = input->ptr; |
| 338 | } |
| 339 | if (!http_7239_extract_nodename(input, nodename, quoted)) |
| 340 | return 0; /* invalid nodename */ |
| 341 | if (istlen(*input) < 1 || *istptr(*input) != ':') { |
| 342 | if (node) |
| 343 | node->nodeport.type = FORWARDED_HEADER_UNK; |
| 344 | goto out; /* no optional port provided */ |
| 345 | } |
| 346 | if (!quoted) |
| 347 | return 0; /* not supported */ |
| 348 | *input = istnext(*input); |
| 349 | if (!http_7239_extract_nodeport(input, nodeport)) |
| 350 | return 0; /* invalid nodeport */ |
| 351 | out: |
| 352 | /* ok */ |
| 353 | if (node) |
| 354 | node->raw.len = input->ptr - node->raw.ptr; |
| 355 | return 1; |
| 356 | } |
| 357 | |
| 358 | static inline int _forwarded_header_save_ctx(struct forwarded_header_ctx *ctx, int current_step, int required_steps) |
| 359 | { |
| 360 | return (ctx && (current_step & required_steps)); |
| 361 | } |
| 362 | |
| 363 | static inline void _forwarded_header_quote_expected(struct ist *hdr, uint8_t *quoted) |
| 364 | { |
| 365 | if (istlen(*hdr) > 0 && *istptr(*hdr) == '"') { |
| 366 | *quoted = 1; |
| 367 | /* node is quoted, we must find corresponding |
| 368 | * ending quote at the end of the token |
| 369 | */ |
| 370 | *hdr = istnext(*hdr); /* skip quote */ |
| 371 | } |
| 372 | } |
| 373 | |
| 374 | /* checks if current header <hdr> is RFC 7239 compliant and can be "trusted". |
| 375 | * function will stop parsing as soon as every <required_steps> have |
| 376 | * been validated or error is encountered. |
| 377 | * Provide FORWARDED_HEADER_ALL for a full header validating spectrum. |
| 378 | * You may provide limited scope to perform quick searches on specific attributes |
| 379 | * If <ctx> is provided (not NULL), parsed attributes will be stored according to |
| 380 | * their types, allowing you to extract some useful information from the header. |
| 381 | * Returns 0 on failure and <validated_steps> bitfield on success. |
| 382 | */ |
| 383 | int http_validate_7239_header(struct ist hdr, int required_steps, struct forwarded_header_ctx *ctx) |
| 384 | { |
| 385 | int validated_steps = 0; |
| 386 | int current_step = 0; |
| 387 | uint8_t first = 1; |
| 388 | uint8_t quoted = 0; |
| 389 | |
| 390 | while (istlen(hdr) && (required_steps & ~validated_steps)) { |
| 391 | if (!first) { |
| 392 | if (*istptr(hdr) == ';') |
| 393 | hdr = istnext(hdr); /* skip ';' */ |
| 394 | else |
| 395 | goto not_ok; /* unexpected char */ |
| 396 | } |
| 397 | else |
| 398 | first = 0; |
| 399 | |
| 400 | if (!(validated_steps & FORWARDED_HEADER_FOR) && istlen(hdr) > 4 && |
| 401 | strncmp("for=", istptr(hdr), 4) == 0) { |
| 402 | struct forwarded_header_node *node = NULL; |
| 403 | |
| 404 | /* for parameter */ |
| 405 | current_step = FORWARDED_HEADER_FOR; |
| 406 | hdr = istadv(hdr, 4); /* skip "for=" */ |
| 407 | _forwarded_header_quote_expected(&hdr, "ed); |
| 408 | if (_forwarded_header_save_ctx(ctx, current_step, required_steps)) |
| 409 | node = &ctx->nfor; |
| 410 | /* validate node */ |
| 411 | if (!http_7239_extract_node(&hdr, node, quoted)) |
| 412 | goto not_ok; /* invalid node */ |
| 413 | } |
| 414 | else if (!(validated_steps & FORWARDED_HEADER_BY) && istlen(hdr) > 3 && |
| 415 | strncmp("by=", istptr(hdr), 3) == 0) { |
| 416 | struct forwarded_header_node *node = NULL; |
| 417 | |
| 418 | /* by parameter */ |
| 419 | current_step = FORWARDED_HEADER_BY; |
| 420 | hdr = istadv(hdr, 3); /* skip "by=" */ |
| 421 | _forwarded_header_quote_expected(&hdr, "ed); |
| 422 | if (_forwarded_header_save_ctx(ctx, current_step, required_steps)) |
| 423 | node = &ctx->nby; |
| 424 | /* validate node */ |
| 425 | if (!http_7239_extract_node(&hdr, node, quoted)) |
| 426 | goto not_ok; /* invalid node */ |
| 427 | } |
| 428 | else if (!(validated_steps & FORWARDED_HEADER_HOST) && istlen(hdr) > 5 && |
| 429 | strncmp("host=", istptr(hdr), 5) == 0) { |
| 430 | struct ist *host = NULL; |
| 431 | |
| 432 | /* host parameter */ |
| 433 | current_step = FORWARDED_HEADER_HOST; |
| 434 | hdr = istadv(hdr, 5); /* skip "host=" */ |
| 435 | _forwarded_header_quote_expected(&hdr, "ed); |
| 436 | if (_forwarded_header_save_ctx(ctx, current_step, required_steps)) |
| 437 | host = &ctx->host; |
| 438 | /* validate host */ |
| 439 | if (!http_7239_extract_host(&hdr, host, quoted)) |
| 440 | goto not_ok; /* invalid host */ |
| 441 | } |
| 442 | else if (!(validated_steps & FORWARDED_HEADER_PROTO) && istlen(hdr) > 6 && |
| 443 | strncmp("proto=", istptr(hdr), 6) == 0) { |
| 444 | /* proto parameter */ |
| 445 | current_step = FORWARDED_HEADER_PROTO; |
| 446 | hdr = istadv(hdr, 6); /* skip "proto=" */ |
| 447 | /* validate proto (only common used http|https are supported for now) */ |
| 448 | if (istlen(hdr) < 4 || strncmp("http", istptr(hdr), 4)) |
| 449 | goto not_ok; |
| 450 | hdr = istadv(hdr, 4); /* skip "http" */ |
| 451 | if (istlen(hdr) && *istptr(hdr) == 's') { |
| 452 | hdr = istnext(hdr); |
| 453 | if (_forwarded_header_save_ctx(ctx, current_step, required_steps)) |
| 454 | ctx->proto = FORWARDED_HEADER_HTTPS; |
| 455 | } else if (_forwarded_header_save_ctx(ctx, current_step, required_steps)) |
| 456 | ctx->proto = FORWARDED_HEADER_HTTP; |
| 457 | /* rfc allows for potential proto quoting, but we don't support |
| 458 | * it: it is not common usage |
| 459 | */ |
| 460 | } |
| 461 | else { |
| 462 | /* not supported |
| 463 | * rfc allows for upcoming extensions |
| 464 | * but obviously, we can't trust them |
| 465 | * as they are not yet standardized |
| 466 | */ |
| 467 | |
| 468 | goto not_ok; |
| 469 | } |
| 470 | /* quote check */ |
| 471 | if (quoted) { |
| 472 | if (istlen(hdr) < 1 || *istptr(hdr) != '"') { |
| 473 | /* matching ending quote not found */ |
| 474 | goto not_ok; |
| 475 | } |
| 476 | hdr = istnext(hdr); /* skip ending quote */ |
| 477 | quoted = 0; /* reset */ |
| 478 | } |
| 479 | validated_steps |= current_step; |
| 480 | } |
| 481 | |
| 482 | return validated_steps; |
| 483 | |
| 484 | not_ok: |
| 485 | return 0; |
| 486 | } |
| 487 | |
Aurelien DARRAGON | 8436c91 | 2023-01-30 09:28:57 +0100 | [diff] [blame] | 488 | static inline void _7239_print_ip6(struct buffer *out, struct in6_addr *ip6_addr, int quoted) |
| 489 | { |
| 490 | char pn[INET6_ADDRSTRLEN]; |
| 491 | |
| 492 | inet_ntop(AF_INET6, |
| 493 | ip6_addr, |
| 494 | pn, sizeof(pn)); |
| 495 | if (!quoted) |
| 496 | chunk_appendf(out, "\""); /* explicit quoting required for ipv6 */ |
| 497 | chunk_appendf(out, "[%s]", pn); |
| 498 | } |
| 499 | |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 500 | static inline void http_build_7239_header_nodename(struct buffer *out, |
| 501 | struct stream *s, struct proxy *curproxy, |
| 502 | const struct sockaddr_storage *addr, |
| 503 | struct http_ext_7239_forby *forby) |
| 504 | { |
| 505 | struct in6_addr *ip6_addr; |
Aurelien DARRAGON | 8436c91 | 2023-01-30 09:28:57 +0100 | [diff] [blame] | 506 | int quoted = !!forby->np_mode; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 507 | |
| 508 | if (forby->nn_mode == HTTP_7239_FORBY_ORIG) { |
| 509 | if (addr && addr->ss_family == AF_INET) { |
| 510 | unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)addr)->sin_addr; |
| 511 | |
| 512 | chunk_appendf(out, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]); |
| 513 | } |
| 514 | else if (addr && addr->ss_family == AF_INET6) { |
| 515 | ip6_addr = &((struct sockaddr_in6 *)addr)->sin6_addr; |
Aurelien DARRAGON | 8436c91 | 2023-01-30 09:28:57 +0100 | [diff] [blame] | 516 | _7239_print_ip6(out, ip6_addr, quoted); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 517 | } |
| 518 | /* else: not supported */ |
| 519 | } |
| 520 | else if (forby->nn_mode == HTTP_7239_FORBY_SMP && forby->nn_expr) { |
| 521 | struct sample *smp; |
| 522 | |
| 523 | smp = sample_process(curproxy, s->sess, s, |
| 524 | SMP_OPT_DIR_REQ | SMP_OPT_FINAL, forby->nn_expr, NULL); |
| 525 | |
| 526 | if (smp) { |
| 527 | if (smp->data.type == SMP_T_IPV6) { |
| 528 | /* smp is valid IP6, print with RFC compliant output */ |
| 529 | ip6_addr = &smp->data.u.ipv6; |
Aurelien DARRAGON | 8436c91 | 2023-01-30 09:28:57 +0100 | [diff] [blame] | 530 | _7239_print_ip6(out, ip6_addr, quoted); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 531 | } |
Aurelien DARRAGON | 8436c91 | 2023-01-30 09:28:57 +0100 | [diff] [blame] | 532 | else if (sample_casts[smp->data.type][SMP_T_STR] && |
| 533 | sample_casts[smp->data.type][SMP_T_STR](smp)) { |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 534 | struct ist validate_n = ist2(smp->data.u.str.area, smp->data.u.str.data); |
| 535 | struct ist validate_o = ist2(smp->data.u.str.area, smp->data.u.str.data); |
| 536 | struct forwarded_header_nodename nodename; |
| 537 | |
| 538 | /* validate nodename */ |
| 539 | if (http_7239_extract_nodename(&validate_n, &nodename, 1) && |
| 540 | !istlen(validate_n)) { |
| 541 | if (nodename.type == FORWARDED_HEADER_IP && |
| 542 | nodename.ip.ss_family == AF_INET6) { |
| 543 | /* special care needed for valid ip6 nodename (quoting) */ |
| 544 | ip6_addr = &((struct sockaddr_in6 *)&nodename.ip)->sin6_addr; |
Aurelien DARRAGON | 8436c91 | 2023-01-30 09:28:57 +0100 | [diff] [blame] | 545 | _7239_print_ip6(out, ip6_addr, quoted); |
| 546 | } else { |
| 547 | /* no special care needed, input is already rfc compliant, |
| 548 | * just print as regular non quoted string |
| 549 | */ |
| 550 | chunk_cat(out, &smp->data.u.str); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 551 | } |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 552 | } |
| 553 | else if (http_7239_extract_obfs(&validate_o, NULL) && |
| 554 | !istlen(validate_o)) { |
| 555 | /* raw user input that should be printed as 7239 obfs */ |
| 556 | chunk_appendf(out, "_%.*s", (int)smp->data.u.str.data, smp->data.u.str.area); |
| 557 | } |
| 558 | /* else: not compliant */ |
| 559 | } |
| 560 | /* else: cannot be casted to str */ |
| 561 | } |
| 562 | /* else: smp error */ |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | static inline void http_build_7239_header_nodeport(struct buffer *out, |
| 567 | struct stream *s, struct proxy *curproxy, |
| 568 | const struct sockaddr_storage *addr, |
| 569 | struct http_ext_7239_forby *forby) |
| 570 | { |
| 571 | if (forby->np_mode == HTTP_7239_FORBY_ORIG) { |
| 572 | if (addr && addr->ss_family == AF_INET) |
| 573 | chunk_appendf(out, "%d", ntohs(((struct sockaddr_in *)addr)->sin_port)); |
| 574 | else if (addr && addr->ss_family == AF_INET6) |
| 575 | chunk_appendf(out, "%d", ntohs(((struct sockaddr_in6 *)addr)->sin6_port)); |
| 576 | /* else: not supported */ |
| 577 | } |
| 578 | else if (forby->np_mode == HTTP_7239_FORBY_SMP && forby->np_expr) { |
| 579 | struct sample *smp; |
| 580 | |
| 581 | smp = sample_fetch_as_type(curproxy, s->sess, s, |
| 582 | SMP_OPT_DIR_REQ | SMP_OPT_FINAL, forby->np_expr, SMP_T_STR); |
| 583 | if (smp) { |
| 584 | struct ist validate_n = ist2(smp->data.u.str.area, smp->data.u.str.data); |
| 585 | struct ist validate_o = ist2(smp->data.u.str.area, smp->data.u.str.data); |
| 586 | |
| 587 | /* validate nodeport */ |
| 588 | if (http_7239_extract_nodeport(&validate_n, NULL) && |
| 589 | !istlen(validate_n)) { |
| 590 | /* no special care needed, input is already rfc compliant, |
| 591 | * just print as regular non quoted string |
| 592 | */ |
| 593 | chunk_cat(out, &smp->data.u.str); |
| 594 | } |
| 595 | else if (http_7239_extract_obfs(&validate_o, NULL) && |
| 596 | !istlen(validate_o)) { |
| 597 | /* raw user input that should be printed as 7239 obfs */ |
| 598 | chunk_appendf(out, "_%.*s", (int)smp->data.u.str.data, smp->data.u.str.area); |
| 599 | } |
| 600 | /* else: not compliant */ |
| 601 | } |
| 602 | /* else: smp error */ |
| 603 | } |
| 604 | } |
| 605 | |
| 606 | static inline void http_build_7239_header_node(struct buffer *out, |
| 607 | struct stream *s, struct proxy *curproxy, |
| 608 | const struct sockaddr_storage *addr, |
| 609 | struct http_ext_7239_forby *forby) |
| 610 | { |
| 611 | size_t offset_start; |
| 612 | size_t offset_save; |
| 613 | |
| 614 | offset_start = out->data; |
| 615 | if (forby->np_mode) |
| 616 | chunk_appendf(out, "\""); |
| 617 | offset_save = out->data; |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 618 | http_build_7239_header_nodename(out, s, curproxy, addr, forby); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 619 | if (offset_save == out->data) { |
| 620 | /* could not build nodename, either because some |
| 621 | * data is not available or user is providing bad input |
| 622 | */ |
| 623 | chunk_appendf(out, "unknown"); |
| 624 | } |
| 625 | if (forby->np_mode) { |
| 626 | chunk_appendf(out, ":"); |
| 627 | offset_save = out->data; |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 628 | http_build_7239_header_nodeport(out, s, curproxy, addr, forby); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 629 | if (offset_save == out->data) { |
| 630 | /* could not build nodeport, either because some data is |
| 631 | * not available or user is providing bad input |
| 632 | */ |
| 633 | out->data = offset_save - 1; |
| 634 | } |
| 635 | } |
| 636 | if (out->data != offset_start && out->area[offset_start] == '"') |
| 637 | chunk_appendf(out, "\""); /* add matching end quote */ |
| 638 | } |
| 639 | |
| 640 | static inline void http_build_7239_header_host(struct buffer *out, |
| 641 | struct stream *s, struct proxy *curproxy, |
| 642 | struct htx *htx, struct http_ext_7239_host *host) |
| 643 | { |
| 644 | struct http_hdr_ctx ctx = { .blk = NULL }; |
| 645 | char *str = NULL; |
| 646 | int str_len = 0; |
| 647 | |
| 648 | if (host->mode == HTTP_7239_HOST_ORIG && |
| 649 | http_find_header(htx, ist("host"), &ctx, 0)) { |
| 650 | str = ctx.value.ptr; |
| 651 | str_len = ctx.value.len; |
| 652 | print_host: |
| 653 | { |
| 654 | struct ist validate = ist2(str, str_len); |
| 655 | /* host check, to ensure rfc compliant output |
| 656 | * (assumming host is quoted/escaped) |
| 657 | */ |
| 658 | if (http_7239_extract_host(&validate, NULL, 1) && !istlen(validate)) |
| 659 | chunk_memcat(out, str, str_len); |
| 660 | /* else: not compliant or partially compliant */ |
| 661 | } |
| 662 | |
| 663 | } |
| 664 | else if (host->mode == HTTP_7239_HOST_SMP && host->expr) { |
| 665 | struct sample *smp; |
| 666 | |
| 667 | smp = sample_fetch_as_type(curproxy, s->sess, s, |
| 668 | SMP_OPT_DIR_REQ | SMP_OPT_FINAL, host->expr, SMP_T_STR); |
| 669 | if (smp) { |
| 670 | str = smp->data.u.str.area; |
| 671 | str_len = smp->data.u.str.data; |
| 672 | goto print_host; |
| 673 | } |
| 674 | /* else: smp error */ |
| 675 | } |
| 676 | } |
| 677 | |
| 678 | /* Tries build 7239 header according to <curproxy> parameters and <s> context |
| 679 | * It both depends on <curproxy>->http_ext->fwd for config and <s> for request |
| 680 | * context data. |
| 681 | * The function will write output to <out> buffer |
| 682 | * Returns 1 for success and 0 for error (ie: not enough space in buffer) |
| 683 | */ |
| 684 | static int http_build_7239_header(struct buffer *out, |
| 685 | struct stream *s, struct proxy *curproxy, struct htx *htx) |
| 686 | { |
| 687 | struct connection *cli_conn = objt_conn(strm_sess(s)->origin); |
| 688 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 689 | if (curproxy->http_ext->fwd->p_proto) { |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 690 | chunk_appendf(out, "%sproto=%s", ((out->data) ? ";" : ""), |
| 691 | ((conn_is_ssl(cli_conn)) ? "https" : "http")); |
| 692 | } |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 693 | if (curproxy->http_ext->fwd->p_host.mode) { |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 694 | /* always add quotes for host parameter to make output compliancy checks simpler */ |
| 695 | chunk_appendf(out, "%shost=\"", ((out->data) ? ";" : "")); |
| 696 | /* ignore return value for now, but could be useful some day */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 697 | http_build_7239_header_host(out, s, curproxy, htx, &curproxy->http_ext->fwd->p_host); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 698 | chunk_appendf(out, "\""); |
| 699 | } |
| 700 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 701 | if (curproxy->http_ext->fwd->p_by.nn_mode) { |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 702 | const struct sockaddr_storage *dst = sc_dst(s->scf); |
| 703 | |
| 704 | chunk_appendf(out, "%sby=", ((out->data) ? ";" : "")); |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 705 | http_build_7239_header_node(out, s, curproxy, dst, &curproxy->http_ext->fwd->p_by); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 706 | } |
| 707 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 708 | if (curproxy->http_ext->fwd->p_for.nn_mode) { |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 709 | const struct sockaddr_storage *src = sc_src(s->scf); |
| 710 | |
| 711 | chunk_appendf(out, "%sfor=", ((out->data) ? ";" : "")); |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 712 | http_build_7239_header_node(out, s, curproxy, src, &curproxy->http_ext->fwd->p_for); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 713 | } |
| 714 | if (unlikely(out->data == out->size)) { |
| 715 | /* not enough space in buffer, error */ |
| 716 | return 0; |
| 717 | } |
| 718 | return 1; |
| 719 | } |
| 720 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 721 | /* This function will try to inject RFC 7239 forwarded header if |
| 722 | * configured on the backend (ignored for frontends). |
| 723 | * Will do nothing if the option is not enabled on the proxy. |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 724 | * Returns 1 for success and 0 for failure |
| 725 | */ |
| 726 | int http_handle_7239_header(struct stream *s, struct channel *req) |
| 727 | { |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 728 | struct proxy *curproxy = s->be; /* ignore frontend */ |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 729 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 730 | if (curproxy->http_ext && curproxy->http_ext->fwd) { |
| 731 | struct htx *htx = htxbuf(&req->buf); |
| 732 | int validate = 1; |
| 733 | struct http_hdr_ctx find = { .blk = NULL }; |
| 734 | struct http_hdr_ctx last = { .blk = NULL}; |
| 735 | struct ist hdr = ist("forwarded"); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 736 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 737 | /* ok, let's build forwarded header */ |
| 738 | chunk_reset(&trash); |
| 739 | if (unlikely(!http_build_7239_header(&trash, s, curproxy, htx))) |
| 740 | return 0; /* error when building header (bad user conf or memory error) */ |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 741 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 742 | /* validate existing forwarded header (including multiple values), |
| 743 | * hard stop if error is encountered |
| 744 | */ |
| 745 | while (http_find_header(htx, hdr, &find, 0)) { |
| 746 | /* validate current header chunk */ |
| 747 | if (!http_validate_7239_header(find.value, FORWARDED_HEADER_ALL, NULL)) { |
| 748 | /* at least one error, existing forwarded header not OK, add our own |
| 749 | * forwarded header, so that it can be trusted |
| 750 | */ |
| 751 | validate = 0; |
| 752 | break; |
| 753 | } |
| 754 | last = find; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 755 | } |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 756 | /* no errors, append our data at the end of existing header */ |
| 757 | if (last.blk && validate) { |
| 758 | if (unlikely(!http_append_header_value(htx, &last, ist2(trash.area, trash.data)))) |
| 759 | return 0; /* htx error */ |
| 760 | } |
| 761 | else { |
| 762 | if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) |
| 763 | return 0; /* htx error */ |
| 764 | } |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 765 | } |
| 766 | return 1; |
| 767 | } |
| 768 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 769 | /* |
| 770 | * add X-Forwarded-For if either the frontend or the backend |
| 771 | * asks for it. |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 772 | * Returns 1 for success and 0 for failure |
| 773 | */ |
| 774 | int http_handle_xff_header(struct stream *s, struct channel *req) |
| 775 | { |
| 776 | struct session *sess = s->sess; |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 777 | struct http_ext_xff *f_xff = NULL; |
| 778 | struct http_ext_xff *b_xff = NULL; |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 779 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 780 | if (sess->fe->http_ext && sess->fe->http_ext->xff) { |
| 781 | /* frontend */ |
| 782 | f_xff = sess->fe->http_ext->xff; |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 783 | } |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 784 | if (s->be->http_ext && s->be->http_ext->xff) { |
| 785 | /* backend */ |
| 786 | b_xff = s->be->http_ext->xff; |
| 787 | } |
| 788 | |
| 789 | if (f_xff || b_xff) { |
| 790 | struct htx *htx = htxbuf(&req->buf); |
| 791 | const struct sockaddr_storage *src = sc_src(s->scf); |
| 792 | struct http_hdr_ctx ctx = { .blk = NULL }; |
| 793 | struct ist hdr = ((b_xff) ? b_xff->hdr_name : f_xff->hdr_name); |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 794 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 795 | if (f_xff && f_xff->mode == HTTP_XFF_IFNONE && |
| 796 | b_xff && b_xff->mode == HTTP_XFF_IFNONE && |
| 797 | http_find_header(htx, hdr, &ctx, 0)) { |
| 798 | /* The header is set to be added only if none is present |
| 799 | * and we found it, so don't do anything. |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 800 | */ |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 801 | } |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 802 | else if (src && src->ss_family == AF_INET) { |
| 803 | /* Add an X-Forwarded-For header unless the source IP is |
| 804 | * in the 'except' network range. |
| 805 | */ |
| 806 | if ((!f_xff || ipcmp2net(src, &f_xff->except_net)) && |
| 807 | (!b_xff || ipcmp2net(src, &b_xff->except_net))) { |
| 808 | unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)src)->sin_addr; |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 809 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 810 | /* Note: we rely on the backend to get the header name to be used for |
| 811 | * x-forwarded-for, because the header is really meant for the backends. |
| 812 | * However, if the backend did not specify any option, we have to rely |
| 813 | * on the frontend's header name. |
| 814 | */ |
| 815 | chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]); |
| 816 | if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) |
| 817 | return 0; |
| 818 | } |
| 819 | } |
| 820 | else if (src && src->ss_family == AF_INET6) { |
| 821 | /* Add an X-Forwarded-For header unless the source IP is |
| 822 | * in the 'except' network range. |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 823 | */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 824 | if ((!f_xff || ipcmp2net(src, &f_xff->except_net)) && |
| 825 | (!b_xff || ipcmp2net(src, &b_xff->except_net))) { |
| 826 | char pn[INET6_ADDRSTRLEN]; |
| 827 | |
| 828 | inet_ntop(AF_INET6, |
| 829 | (const void *)&((struct sockaddr_in6 *)(src))->sin6_addr, |
| 830 | pn, sizeof(pn)); |
| 831 | |
| 832 | /* Note: we rely on the backend to get the header name to be used for |
| 833 | * x-forwarded-for, because the header is really meant for the backends. |
| 834 | * However, if the backend did not specify any option, we have to rely |
| 835 | * on the frontend's header name. |
| 836 | */ |
| 837 | chunk_printf(&trash, "%s", pn); |
| 838 | if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) |
| 839 | return 0; |
| 840 | } |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 841 | } |
| 842 | } |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 843 | return 1; |
| 844 | } |
| 845 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 846 | /* |
| 847 | * add X-Original-To if either the frontend or the backend |
| 848 | * asks for it. |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 849 | * Returns 1 for success and 0 for failure |
| 850 | */ |
| 851 | int http_handle_xot_header(struct stream *s, struct channel *req) |
| 852 | { |
| 853 | struct session *sess = s->sess; |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 854 | struct http_ext_xot *f_xot = NULL; |
| 855 | struct http_ext_xot *b_xot = NULL; |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 856 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 857 | if (sess->fe->http_ext && sess->fe->http_ext->xot) { |
| 858 | /* frontend */ |
| 859 | f_xot = sess->fe->http_ext->xot; |
| 860 | } |
| 861 | if (s->be->http_ext && s->be->http_ext->xot) { |
| 862 | /* backend */ |
| 863 | BUG_ON(!s->be->http_ext); |
| 864 | b_xot = s->be->http_ext->xot; |
| 865 | } |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 866 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 867 | if (f_xot || b_xot) { |
| 868 | struct htx *htx = htxbuf(&req->buf); |
| 869 | const struct sockaddr_storage *dst = sc_dst(s->scf); |
| 870 | struct ist hdr = ((b_xot) ? b_xot->hdr_name : f_xot->hdr_name); |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 871 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 872 | if (dst && dst->ss_family == AF_INET) { |
| 873 | /* Add an X-Original-To header unless the destination IP is |
| 874 | * in the 'except' network range. |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 875 | */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 876 | if ((!f_xot || ipcmp2net(dst, &f_xot->except_net)) && |
| 877 | (!b_xot || ipcmp2net(dst, &b_xot->except_net))) { |
| 878 | unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)dst)->sin_addr; |
| 879 | |
| 880 | /* Note: we rely on the backend to get the header name to be used for |
| 881 | * x-original-to, because the header is really meant for the backends. |
| 882 | * However, if the backend did not specify any option, we have to rely |
| 883 | * on the frontend's header name. |
| 884 | */ |
| 885 | chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]); |
| 886 | if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) |
| 887 | return 0; |
| 888 | } |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 889 | } |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 890 | else if (dst && dst->ss_family == AF_INET6) { |
| 891 | /* Add an X-Original-To header unless the source IP is |
| 892 | * in the 'except' network range. |
| 893 | */ |
| 894 | if ((!f_xot || ipcmp2net(dst, &f_xot->except_net)) && |
| 895 | (!b_xot || ipcmp2net(dst, &b_xot->except_net))) { |
| 896 | char pn[INET6_ADDRSTRLEN]; |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 897 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 898 | inet_ntop(AF_INET6, |
| 899 | (const void *)&((struct sockaddr_in6 *)dst)->sin6_addr, |
| 900 | pn, sizeof(pn)); |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 901 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 902 | /* Note: we rely on the backend to get the header name to be used for |
| 903 | * x-forwarded-for, because the header is really meant for the backends. |
| 904 | * However, if the backend did not specify any option, we have to rely |
| 905 | * on the frontend's header name. |
| 906 | */ |
| 907 | chunk_printf(&trash, "%s", pn); |
| 908 | if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data)))) |
| 909 | return 0; |
| 910 | } |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 911 | } |
| 912 | } |
| 913 | return 1; |
| 914 | } |
| 915 | |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 916 | /* |
| 917 | * =========== CONFIG =========== |
| 918 | * below are helpers to parse http ext options from the config |
| 919 | */ |
| 920 | static int proxy_http_parse_oom(const char *file, int linenum) |
| 921 | { |
| 922 | int err_code = 0; |
| 923 | |
| 924 | ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); |
| 925 | err_code |= ERR_ALERT | ERR_ABORT; |
| 926 | return err_code; |
| 927 | } |
| 928 | |
| 929 | static inline int _proxy_http_parse_7239_expr(char **args, int *cur_arg, |
| 930 | const char *file, int linenum, |
| 931 | char **expr_s) |
| 932 | { |
| 933 | int err_code = 0; |
| 934 | |
| 935 | if (!*args[*cur_arg + 1]) { |
| 936 | ha_alert("parsing [%s:%d]: '%s' expects <expr> as argument.\n", |
| 937 | file, linenum, args[*cur_arg]); |
| 938 | err_code |= ERR_ALERT | ERR_FATAL; |
| 939 | goto out; |
| 940 | } |
| 941 | *cur_arg += 1; |
| 942 | ha_free(expr_s); |
| 943 | *expr_s = strdup(args[*cur_arg]); |
| 944 | if (!*expr_s) |
| 945 | return proxy_http_parse_oom(file, linenum); |
| 946 | *cur_arg += 1; |
| 947 | out: |
| 948 | return err_code; |
| 949 | } |
| 950 | |
| 951 | int proxy_http_parse_7239(char **args, int cur_arg, |
| 952 | struct proxy *curproxy, const struct proxy *defpx, |
| 953 | const char *file, int linenum) |
| 954 | { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 955 | struct http_ext_7239 *fwd; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 956 | int err_code = 0; |
| 957 | |
| 958 | if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, "option forwarded", NULL)) { |
| 959 | /* option is ignored for frontends */ |
| 960 | err_code |= ERR_WARN; |
| 961 | goto out; |
| 962 | } |
| 963 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 964 | if (!http_ext_7239_prepare(curproxy)) |
| 965 | return proxy_http_parse_oom(file, linenum); |
| 966 | |
| 967 | fwd = curproxy->http_ext->fwd; |
| 968 | |
| 969 | fwd->p_proto = 0; |
| 970 | fwd->p_host.mode = 0; |
| 971 | fwd->p_for.nn_mode = 0; |
| 972 | fwd->p_for.np_mode = 0; |
| 973 | fwd->p_by.nn_mode = 0; |
| 974 | fwd->p_by.np_mode = 0; |
| 975 | ha_free(&fwd->c_file); |
| 976 | fwd->c_file = strdup(file); |
| 977 | fwd->c_line = linenum; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 978 | |
| 979 | /* start at 2, since 0+1 = "option" "forwarded" */ |
| 980 | cur_arg = 2; |
| 981 | if (!*(args[cur_arg])) { |
| 982 | /* no optional argument provided, use default settings */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 983 | fwd->p_for.nn_mode = HTTP_7239_FORBY_ORIG; /* enable for and mimic xff */ |
| 984 | fwd->p_proto = 1; /* enable proto */ |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 985 | goto out; |
| 986 | } |
| 987 | /* loop to go through optional arguments */ |
| 988 | while (*(args[cur_arg])) { |
| 989 | if (strcmp(args[cur_arg], "proto") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 990 | fwd->p_proto = 1; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 991 | cur_arg += 1; |
| 992 | } else if (strcmp(args[cur_arg], "host") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 993 | fwd->p_host.mode = HTTP_7239_HOST_ORIG; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 994 | cur_arg += 1; |
| 995 | } else if (strcmp(args[cur_arg], "host-expr") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 996 | fwd->p_host.mode = HTTP_7239_HOST_SMP; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 997 | err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum, |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 998 | &fwd->p_host.expr_s); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 999 | if (err_code & ERR_FATAL) |
| 1000 | goto out; |
| 1001 | } else if (strcmp(args[cur_arg], "by") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1002 | fwd->p_by.nn_mode = HTTP_7239_FORBY_ORIG; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1003 | cur_arg += 1; |
| 1004 | } else if (strcmp(args[cur_arg], "by-expr") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1005 | fwd->p_by.nn_mode = HTTP_7239_FORBY_SMP; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1006 | err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum, |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1007 | &fwd->p_by.nn_expr_s); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1008 | if (err_code & ERR_FATAL) |
| 1009 | goto out; |
| 1010 | } else if (strcmp(args[cur_arg], "for") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1011 | fwd->p_for.nn_mode = HTTP_7239_FORBY_ORIG; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1012 | cur_arg += 1; |
| 1013 | } else if (strcmp(args[cur_arg], "for-expr") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1014 | fwd->p_for.nn_mode = HTTP_7239_FORBY_SMP; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1015 | err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum, |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1016 | &fwd->p_for.nn_expr_s); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1017 | if (err_code & ERR_FATAL) |
| 1018 | goto out; |
| 1019 | } else if (strcmp(args[cur_arg], "by_port") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1020 | fwd->p_by.np_mode = HTTP_7239_FORBY_ORIG; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1021 | cur_arg += 1; |
| 1022 | } else if (strcmp(args[cur_arg], "by_port-expr") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1023 | fwd->p_by.np_mode = HTTP_7239_FORBY_SMP; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1024 | err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum, |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1025 | &fwd->p_by.np_expr_s); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1026 | if (err_code & ERR_FATAL) |
| 1027 | goto out; |
| 1028 | } else if (strcmp(args[cur_arg], "for_port") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1029 | fwd->p_for.np_mode = HTTP_7239_FORBY_ORIG; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1030 | cur_arg += 1; |
| 1031 | } else if (strcmp(args[cur_arg], "for_port-expr") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1032 | fwd->p_for.np_mode = HTTP_7239_FORBY_SMP; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1033 | err_code |= _proxy_http_parse_7239_expr(args, &cur_arg, file, linenum, |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1034 | &fwd->p_for.np_expr_s); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1035 | if (err_code & ERR_FATAL) |
| 1036 | goto out; |
| 1037 | } else { |
| 1038 | /* unknown suboption - catchall */ |
| 1039 | ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'proto', 'host', " |
| 1040 | "'host-expr', 'by', 'by-expr', 'by_port', 'by_port-expr', " |
| 1041 | "'for', 'for-expr', 'for_port' and 'for_port-expr'.\n", |
| 1042 | file, linenum, args[0], args[1]); |
| 1043 | err_code |= ERR_ALERT | ERR_FATAL; |
| 1044 | goto out; |
| 1045 | } |
| 1046 | } /* end while loop */ |
| 1047 | |
| 1048 | /* consistency check */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1049 | if (fwd->p_by.np_mode && |
| 1050 | !fwd->p_by.nn_mode) { |
| 1051 | fwd->p_by.np_mode = 0; |
| 1052 | ha_free(&fwd->p_by.np_expr_s); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1053 | ha_warning("parsing [%s:%d] : '%s %s' : '%s' will be ignored because both 'by' " |
| 1054 | "and 'by-expr' are unset\n", |
| 1055 | file, linenum, args[0], args[1], |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1056 | ((fwd->p_by.np_mode == HTTP_7239_FORBY_ORIG) ? "by_port" : "by_port-expr")); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1057 | err_code |= ERR_WARN; |
| 1058 | } |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1059 | if (fwd->p_for.np_mode && |
| 1060 | !fwd->p_for.nn_mode) { |
| 1061 | fwd->p_for.np_mode = 0; |
| 1062 | ha_free(&fwd->p_for.np_expr_s); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1063 | ha_warning("parsing [%s:%d] : '%s %s' : '%s' will be ignored because both 'for' " |
| 1064 | "and 'for-expr' are unset\n", |
| 1065 | file, linenum, args[0], args[1], |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1066 | ((fwd->p_for.np_mode == HTTP_7239_FORBY_ORIG) ? "for_port" : "for_port-expr")); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1067 | err_code |= ERR_WARN; |
| 1068 | } |
| 1069 | |
| 1070 | out: |
| 1071 | return err_code; |
| 1072 | } |
| 1073 | |
Aurelien DARRAGON | c07001b | 2023-01-24 17:55:09 +0100 | [diff] [blame] | 1074 | /* rfc7239 forwarded option needs a postparsing step |
| 1075 | * to convert parsing hints into runtime usable sample expressions |
| 1076 | * Returns a composition of ERR_NONE, ERR_FATAL, ERR_ALERT, ERR_WARN |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1077 | */ |
| 1078 | int proxy_http_compile_7239(struct proxy *curproxy) |
| 1079 | { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1080 | struct http_ext_7239 *fwd; |
Aurelien DARRAGON | c07001b | 2023-01-24 17:55:09 +0100 | [diff] [blame] | 1081 | int err = ERR_NONE; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1082 | int loop; |
| 1083 | |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1084 | if (!(curproxy->cap & PR_CAP_BE)) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1085 | /* no backend cap: not supported (ie: frontend) */ |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1086 | goto out; |
| 1087 | } |
| 1088 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1089 | /* should not happen (test should be performed after BE cap test) */ |
| 1090 | BUG_ON(!curproxy->http_ext || !curproxy->http_ext->fwd); |
| 1091 | |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1092 | curproxy->conf.args.ctx = ARGC_OPT; /* option */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1093 | curproxy->conf.args.file = curproxy->http_ext->fwd->c_file; |
| 1094 | curproxy->conf.args.line = curproxy->http_ext->fwd->c_line; |
| 1095 | fwd = curproxy->http_ext->fwd; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1096 | |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1097 | /* it is important that we keep iterating on error to make sure |
| 1098 | * all fwd config fields are in the same state (post-parsing state) |
| 1099 | */ |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1100 | for (loop = 0; loop < 5; loop++) { |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1101 | char **expr_str = NULL; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1102 | struct sample_expr **expr = NULL; |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1103 | struct sample_expr *cur_expr; |
Aurelien DARRAGON | c07001b | 2023-01-24 17:55:09 +0100 | [diff] [blame] | 1104 | char *err_str = NULL; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1105 | int smp = 0; |
| 1106 | int idx = 0; |
| 1107 | |
| 1108 | switch (loop) { |
| 1109 | case 0: |
| 1110 | /* host */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1111 | expr_str = &fwd->p_host.expr_s; |
| 1112 | expr = &fwd->p_host.expr; |
| 1113 | smp = (fwd->p_host.mode == HTTP_7239_HOST_SMP); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1114 | break; |
| 1115 | case 1: |
| 1116 | /* by->node */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1117 | expr_str = &fwd->p_by.nn_expr_s; |
| 1118 | expr = &fwd->p_by.nn_expr; |
| 1119 | smp = (fwd->p_by.nn_mode == HTTP_7239_FORBY_SMP); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1120 | break; |
| 1121 | case 2: |
| 1122 | /* by->nodeport */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1123 | expr_str = &fwd->p_by.np_expr_s; |
| 1124 | expr = &fwd->p_by.np_expr; |
| 1125 | smp = (fwd->p_by.np_mode == HTTP_7239_FORBY_SMP); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1126 | break; |
| 1127 | case 3: |
| 1128 | /* for->node */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1129 | expr_str = &fwd->p_for.nn_expr_s; |
| 1130 | expr = &fwd->p_for.nn_expr; |
| 1131 | smp = (fwd->p_for.nn_mode == HTTP_7239_FORBY_SMP); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1132 | break; |
| 1133 | case 4: |
| 1134 | /* for->nodeport */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1135 | expr_str = &fwd->p_for.np_expr_s; |
| 1136 | expr = &fwd->p_for.np_expr; |
| 1137 | smp = (fwd->p_for.np_mode == HTTP_7239_FORBY_SMP); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1138 | break; |
| 1139 | } |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1140 | if (!smp) |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1141 | continue; /* no expr */ |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1142 | |
| 1143 | /* expr and expr_str cannot be NULL past this point */ |
| 1144 | BUG_ON(!expr || !expr_str); |
| 1145 | |
| 1146 | if (!*expr_str) { |
| 1147 | /* should not happen unless system memory exhaustion */ |
| 1148 | ha_alert("%s '%s' [%s:%d]: failed to parse 'option forwarded' expression : %s.\n", |
| 1149 | proxy_type_str(curproxy), curproxy->id, |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1150 | fwd->c_file, fwd->c_line, |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1151 | "memory error"); |
Aurelien DARRAGON | c07001b | 2023-01-24 17:55:09 +0100 | [diff] [blame] | 1152 | err |= ERR_ALERT | ERR_FATAL; |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1153 | continue; |
| 1154 | } |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1155 | |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1156 | cur_expr = |
| 1157 | sample_parse_expr((char*[]){*expr_str, NULL}, &idx, |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1158 | fwd->c_file, |
| 1159 | fwd->c_line, |
Aurelien DARRAGON | c07001b | 2023-01-24 17:55:09 +0100 | [diff] [blame] | 1160 | &err_str, &curproxy->conf.args, NULL); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1161 | |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1162 | if (!cur_expr) { |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1163 | ha_alert("%s '%s' [%s:%d]: failed to parse 'option forwarded' expression '%s' in : %s.\n", |
| 1164 | proxy_type_str(curproxy), curproxy->id, |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1165 | fwd->c_file, fwd->c_line, |
Aurelien DARRAGON | c07001b | 2023-01-24 17:55:09 +0100 | [diff] [blame] | 1166 | *expr_str, err_str); |
| 1167 | ha_free(&err_str); |
| 1168 | err |= ERR_ALERT | ERR_FATAL; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1169 | } |
Aurelien DARRAGON | d745a3f | 2023-01-06 15:06:49 +0100 | [diff] [blame] | 1170 | else if (!(cur_expr->fetch->val & SMP_VAL_BE_HRQ_HDR)) { |
| 1171 | /* fetch not available in this context: sample expr is resolved |
| 1172 | * within backend right after headers are processed. |
| 1173 | * (in http_process_request()) |
| 1174 | * -> we simply warn the user about the misuse |
| 1175 | */ |
| 1176 | ha_warning("%s '%s' [%s:%d]: in 'option forwarded' sample expression '%s' : " |
| 1177 | "some args extract information from '%s', " |
| 1178 | "none of which is available here.\n", |
| 1179 | proxy_type_str(curproxy), curproxy->id, |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1180 | fwd->c_file, fwd->c_line, |
Aurelien DARRAGON | d745a3f | 2023-01-06 15:06:49 +0100 | [diff] [blame] | 1181 | *expr_str, sample_ckp_names(cur_expr->fetch->use)); |
Aurelien DARRAGON | c07001b | 2023-01-24 17:55:09 +0100 | [diff] [blame] | 1182 | err |= ERR_WARN; |
Aurelien DARRAGON | d745a3f | 2023-01-06 15:06:49 +0100 | [diff] [blame] | 1183 | } |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1184 | /* post parsing individual expr cleanup */ |
| 1185 | ha_free(expr_str); |
| 1186 | |
| 1187 | /* expr assignment */ |
| 1188 | *expr = cur_expr; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1189 | } |
| 1190 | curproxy->conf.args.file = NULL; |
| 1191 | curproxy->conf.args.line = 0; |
| 1192 | |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1193 | /* post parsing general cleanup */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1194 | ha_free(&fwd->c_file); |
| 1195 | fwd->c_line = 0; |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1196 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1197 | fwd->c_mode = 1; /* parsing completed */ |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1198 | |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1199 | out: |
Aurelien DARRAGON | c07001b | 2023-01-24 17:55:09 +0100 | [diff] [blame] | 1200 | return err; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1201 | } |
| 1202 | |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1203 | /* x-forwarded-for */ |
| 1204 | int proxy_http_parse_xff(char **args, int cur_arg, |
| 1205 | struct proxy *curproxy, const struct proxy *defpx, |
| 1206 | const char *file, int linenum) |
| 1207 | { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1208 | struct http_ext_xff *xff; |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1209 | int err_code = 0; |
| 1210 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1211 | if (!http_ext_xff_prepare(curproxy)) |
| 1212 | return proxy_http_parse_oom(file, linenum); |
| 1213 | |
| 1214 | xff = curproxy->http_ext->xff; |
| 1215 | |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1216 | /* insert x-forwarded-for field, but not for the IP address listed as an except. |
| 1217 | * set default options (ie: bitfield, header name, etc) |
| 1218 | */ |
| 1219 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1220 | xff->mode = HTTP_XFF_ALWAYS; |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1221 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1222 | istfree(&xff->hdr_name); |
| 1223 | xff->hdr_name = istdup(ist(DEF_XFORWARDFOR_HDR)); |
| 1224 | if (!isttest(xff->hdr_name)) |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1225 | return proxy_http_parse_oom(file, linenum); |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1226 | xff->except_net.family = AF_UNSPEC; |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1227 | |
| 1228 | /* loop to go through arguments - start at 2, since 0+1 = "option" "forwardfor" */ |
| 1229 | cur_arg = 2; |
| 1230 | while (*(args[cur_arg])) { |
| 1231 | if (strcmp(args[cur_arg], "except") == 0) { |
| 1232 | unsigned char mask; |
| 1233 | int i; |
| 1234 | |
| 1235 | /* suboption except - needs additional argument for it */ |
| 1236 | if (*(args[cur_arg+1]) && |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1237 | str2net(args[cur_arg+1], 1, &xff->except_net.addr.v4.ip, &xff->except_net.addr.v4.mask)) { |
| 1238 | xff->except_net.family = AF_INET; |
| 1239 | xff->except_net.addr.v4.ip.s_addr &= xff->except_net.addr.v4.mask.s_addr; |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1240 | } |
| 1241 | else if (*(args[cur_arg+1]) && |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1242 | str62net(args[cur_arg+1], &xff->except_net.addr.v6.ip, &mask)) { |
| 1243 | xff->except_net.family = AF_INET6; |
| 1244 | len2mask6(mask, &xff->except_net.addr.v6.mask); |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1245 | for (i = 0; i < 16; i++) |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1246 | xff->except_net.addr.v6.ip.s6_addr[i] &= xff->except_net.addr.v6.mask.s6_addr[i]; |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1247 | } |
| 1248 | else { |
| 1249 | ha_alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n", |
| 1250 | file, linenum, args[0], args[1], args[cur_arg]); |
| 1251 | err_code |= ERR_ALERT | ERR_FATAL; |
| 1252 | goto out; |
| 1253 | } |
| 1254 | /* flush useless bits */ |
| 1255 | cur_arg += 2; |
| 1256 | } else if (strcmp(args[cur_arg], "header") == 0) { |
| 1257 | /* suboption header - needs additional argument for it */ |
| 1258 | if (*(args[cur_arg+1]) == 0) { |
| 1259 | ha_alert("parsing [%s:%d] : '%s %s %s' expects <header_name> as argument.\n", |
| 1260 | file, linenum, args[0], args[1], args[cur_arg]); |
| 1261 | err_code |= ERR_ALERT | ERR_FATAL; |
| 1262 | goto out; |
| 1263 | } |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1264 | istfree(&xff->hdr_name); |
| 1265 | xff->hdr_name = istdup(ist(args[cur_arg+1])); |
| 1266 | if (!isttest(xff->hdr_name)) |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1267 | return proxy_http_parse_oom(file, linenum); |
| 1268 | cur_arg += 2; |
| 1269 | } else if (strcmp(args[cur_arg], "if-none") == 0) { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1270 | xff->mode = HTTP_XFF_IFNONE; |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1271 | cur_arg += 1; |
| 1272 | } else { |
| 1273 | /* unknown suboption - catchall */ |
| 1274 | ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'except', 'header' and 'if-none'.\n", |
| 1275 | file, linenum, args[0], args[1]); |
| 1276 | err_code |= ERR_ALERT | ERR_FATAL; |
| 1277 | goto out; |
| 1278 | } |
| 1279 | } /* end while loop */ |
| 1280 | out: |
| 1281 | return err_code; |
| 1282 | } |
| 1283 | |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1284 | /* x-original-to */ |
| 1285 | int proxy_http_parse_xot(char **args, int cur_arg, |
| 1286 | struct proxy *curproxy, const struct proxy *defpx, |
| 1287 | const char *file, int linenum) |
| 1288 | { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1289 | struct http_ext_xot *xot; |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1290 | int err_code = 0; |
| 1291 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1292 | if (!http_ext_xot_prepare(curproxy)) |
| 1293 | return proxy_http_parse_oom(file, linenum); |
| 1294 | |
| 1295 | xot = curproxy->http_ext->xot; |
| 1296 | |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1297 | /* insert x-original-to field, but not for the IP address listed as an except. |
| 1298 | * set default options (ie: bitfield, header name, etc) |
| 1299 | */ |
| 1300 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1301 | istfree(&xot->hdr_name); |
| 1302 | xot->hdr_name = istdup(ist(DEF_XORIGINALTO_HDR)); |
| 1303 | if (!isttest(xot->hdr_name)) |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1304 | return proxy_http_parse_oom(file, linenum); |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1305 | xot->except_net.family = AF_UNSPEC; |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1306 | |
| 1307 | /* loop to go through arguments - start at 2, since 0+1 = "option" "originalto" */ |
| 1308 | cur_arg = 2; |
| 1309 | while (*(args[cur_arg])) { |
| 1310 | if (strcmp(args[cur_arg], "except") == 0) { |
| 1311 | unsigned char mask; |
| 1312 | int i; |
| 1313 | |
| 1314 | /* suboption except - needs additional argument for it */ |
| 1315 | if (*(args[cur_arg+1]) && |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1316 | str2net(args[cur_arg+1], 1, &xot->except_net.addr.v4.ip, &xot->except_net.addr.v4.mask)) { |
| 1317 | xot->except_net.family = AF_INET; |
| 1318 | xot->except_net.addr.v4.ip.s_addr &= xot->except_net.addr.v4.mask.s_addr; |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1319 | } |
| 1320 | else if (*(args[cur_arg+1]) && |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1321 | str62net(args[cur_arg+1], &xot->except_net.addr.v6.ip, &mask)) { |
| 1322 | xot->except_net.family = AF_INET6; |
| 1323 | len2mask6(mask, &xot->except_net.addr.v6.mask); |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1324 | for (i = 0; i < 16; i++) |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1325 | xot->except_net.addr.v6.ip.s6_addr[i] &= xot->except_net.addr.v6.mask.s6_addr[i]; |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1326 | } |
| 1327 | else { |
| 1328 | ha_alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n", |
| 1329 | file, linenum, args[0], args[1], args[cur_arg]); |
| 1330 | err_code |= ERR_ALERT | ERR_FATAL; |
| 1331 | goto out; |
| 1332 | } |
| 1333 | cur_arg += 2; |
| 1334 | } else if (strcmp(args[cur_arg], "header") == 0) { |
| 1335 | /* suboption header - needs additional argument for it */ |
| 1336 | if (*(args[cur_arg+1]) == 0) { |
| 1337 | ha_alert("parsing [%s:%d] : '%s %s %s' expects <header_name> as argument.\n", |
| 1338 | file, linenum, args[0], args[1], args[cur_arg]); |
| 1339 | err_code |= ERR_ALERT | ERR_FATAL; |
| 1340 | goto out; |
| 1341 | } |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1342 | istfree(&xot->hdr_name); |
| 1343 | xot->hdr_name = istdup(ist(args[cur_arg+1])); |
| 1344 | if (!isttest(xot->hdr_name)) |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1345 | return proxy_http_parse_oom(file, linenum); |
| 1346 | cur_arg += 2; |
| 1347 | } else { |
| 1348 | /* unknown suboption - catchall */ |
| 1349 | ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'except' and 'header'.\n", |
| 1350 | file, linenum, args[0], args[1]); |
| 1351 | err_code |= ERR_ALERT | ERR_FATAL; |
| 1352 | goto out; |
| 1353 | } |
| 1354 | } /* end while loop */ |
| 1355 | |
| 1356 | out: |
| 1357 | return err_code; |
| 1358 | } |
| 1359 | |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1360 | /* |
| 1361 | * =========== MGMT =========== |
| 1362 | * below are helpers to manage http ext options |
| 1363 | */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1364 | |
| 1365 | /* Ensure http_ext->fwd is properly allocated and |
| 1366 | * initialized for <curproxy>. |
| 1367 | * The function will leverage http_ext_prepare() to make |
| 1368 | * sure http_ext is properly allocated and initialized as well. |
| 1369 | * Returns 1 for success and 0 for failure (memory error) |
| 1370 | */ |
| 1371 | int http_ext_7239_prepare(struct proxy *curproxy) |
| 1372 | { |
| 1373 | struct http_ext_7239 *fwd; |
| 1374 | |
| 1375 | if (!http_ext_prepare(curproxy)) |
| 1376 | return 0; |
| 1377 | if (curproxy->http_ext->fwd) |
| 1378 | return 1; /* nothing to do */ |
| 1379 | |
| 1380 | fwd = malloc(sizeof(*fwd)); |
| 1381 | if (!fwd) |
| 1382 | return 0; |
| 1383 | /* initialize fwd mandatory fields */ |
| 1384 | fwd->c_mode = 0; /* pre-compile (parse) time */ |
| 1385 | fwd->c_file = NULL; |
| 1386 | fwd->p_host.expr_s = NULL; |
| 1387 | fwd->p_by.nn_expr_s = NULL; |
| 1388 | fwd->p_by.np_expr_s = NULL; |
| 1389 | fwd->p_for.nn_expr_s = NULL; |
| 1390 | fwd->p_for.np_expr_s = NULL; |
| 1391 | /* assign */ |
| 1392 | curproxy->http_ext->fwd = fwd; |
| 1393 | return 1; |
| 1394 | } |
| 1395 | |
| 1396 | /* Ensure http_ext->xff is properly allocated and |
| 1397 | * initialized for <curproxy>. |
| 1398 | * The function will leverage http_ext_prepare() to make |
| 1399 | * sure http_ext is properly allocated and initialized as well. |
| 1400 | * Returns 1 for success and 0 for failure (memory error) |
| 1401 | */ |
| 1402 | int http_ext_xff_prepare(struct proxy *curproxy) |
| 1403 | { |
| 1404 | struct http_ext_xff *xff; |
| 1405 | |
| 1406 | if (!http_ext_prepare(curproxy)) |
| 1407 | return 0; |
| 1408 | if (curproxy->http_ext->xff) |
| 1409 | return 1; /* nothing to do */ |
| 1410 | |
| 1411 | xff = malloc(sizeof(*xff)); |
| 1412 | if (!xff) |
| 1413 | return 0; |
| 1414 | /* initialize xff mandatory fields */ |
| 1415 | xff->hdr_name = IST_NULL; |
| 1416 | /* assign */ |
| 1417 | curproxy->http_ext->xff = xff; |
| 1418 | return 1; |
| 1419 | } |
| 1420 | |
| 1421 | /* Ensure http_ext->xot is properly allocated and |
| 1422 | * initialized for <curproxy>. |
| 1423 | * The function will leverage http_ext_prepare() to make |
| 1424 | * sure http_ext is properly allocated and initialized as well. |
| 1425 | * Returns 1 for success and 0 for failure (memory error) |
| 1426 | */ |
| 1427 | int http_ext_xot_prepare(struct proxy *curproxy) |
| 1428 | { |
| 1429 | struct http_ext_xot *xot; |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1430 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1431 | if (!http_ext_prepare(curproxy)) |
| 1432 | return 0; |
| 1433 | if (curproxy->http_ext->xot) |
| 1434 | return 1; /* nothing to do */ |
| 1435 | |
| 1436 | xot = malloc(sizeof(*xot)); |
| 1437 | if (!xot) |
| 1438 | return 0; |
| 1439 | /* initialize xot mandatory fields */ |
| 1440 | xot->hdr_name = IST_NULL; |
| 1441 | /* assign */ |
| 1442 | curproxy->http_ext->xot = xot; |
| 1443 | return 1; |
| 1444 | } |
| 1445 | |
| 1446 | /* deep clean http_ext->fwd parameter for <curproxy> |
| 1447 | * http_ext->fwd will be freed |
| 1448 | * clean behavior will differ depending on http_ext->fwd |
| 1449 | * state. If fwd is in 'parsed' state, parsing hints will be |
| 1450 | * cleaned. Else, it means fwd is in 'compiled' state, in this |
| 1451 | * case we're cleaning compiled results. |
| 1452 | * This is because parse and compile memory areas are shared in |
| 1453 | * a single union to optimize struct http_ext_7239 size. |
| 1454 | */ |
| 1455 | void http_ext_7239_clean(struct proxy *curproxy) |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1456 | { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1457 | struct http_ext_7239 *clean; |
| 1458 | |
| 1459 | if (!curproxy->http_ext) |
| 1460 | return; |
| 1461 | clean = curproxy->http_ext->fwd; |
| 1462 | if (!clean) |
| 1463 | return; /* nothing to do */ |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1464 | if (!clean->c_mode) { |
| 1465 | /* parsed */ |
| 1466 | ha_free(&clean->c_file); |
| 1467 | ha_free(&clean->p_host.expr_s); |
| 1468 | ha_free(&clean->p_by.nn_expr_s); |
| 1469 | ha_free(&clean->p_by.np_expr_s); |
| 1470 | ha_free(&clean->p_for.nn_expr_s); |
| 1471 | ha_free(&clean->p_for.np_expr_s); |
| 1472 | } |
| 1473 | else { |
| 1474 | /* compiled */ |
| 1475 | release_sample_expr(clean->p_host.expr); |
| 1476 | clean->p_host.expr = NULL; |
| 1477 | release_sample_expr(clean->p_by.nn_expr); |
| 1478 | clean->p_by.nn_expr = NULL; |
| 1479 | release_sample_expr(clean->p_by.np_expr); |
| 1480 | clean->p_by.np_expr = NULL; |
| 1481 | release_sample_expr(clean->p_for.nn_expr); |
| 1482 | clean->p_for.nn_expr = NULL; |
| 1483 | release_sample_expr(clean->p_for.np_expr); |
| 1484 | clean->p_for.np_expr = NULL; |
| 1485 | } |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1486 | /* free fwd */ |
| 1487 | ha_free(&curproxy->http_ext->fwd); |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1488 | } |
| 1489 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1490 | /* deep clean http_ext->xff parameter for <curproxy> |
| 1491 | * http_ext->xff will be freed |
| 1492 | */ |
| 1493 | void http_ext_xff_clean(struct proxy *curproxy) |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1494 | { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1495 | struct http_ext_xff *clean; |
| 1496 | |
| 1497 | if (!curproxy->http_ext) |
| 1498 | return; |
| 1499 | clean = curproxy->http_ext->xff; |
| 1500 | if (!clean) |
| 1501 | return; /* nothing to do */ |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1502 | istfree(&clean->hdr_name); |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1503 | /* free xff */ |
| 1504 | ha_free(&curproxy->http_ext->xff); |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1505 | } |
| 1506 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1507 | /* deep clean http_ext->xot parameter for <curproxy> |
| 1508 | * http_ext->xot will be freed |
| 1509 | */ |
| 1510 | void http_ext_xot_clean(struct proxy *curproxy) |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1511 | { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1512 | struct http_ext_xot *clean; |
| 1513 | |
| 1514 | if (!curproxy->http_ext) |
| 1515 | return; |
| 1516 | clean = curproxy->http_ext->xot; |
| 1517 | if (!clean) |
| 1518 | return; /* nothing to do */ |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1519 | istfree(&clean->hdr_name); |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1520 | /* free xot */ |
| 1521 | ha_free(&curproxy->http_ext->xot); |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1522 | } |
| 1523 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1524 | /* duplicate http_ext->fwd parameters from <def> to <cpy> |
| 1525 | * performs the required memory allocation and initialization |
| 1526 | */ |
| 1527 | void http_ext_7239_dup(const struct proxy *def, struct proxy *cpy) |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1528 | { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1529 | struct http_ext_7239 *dest = NULL; |
| 1530 | struct http_ext_7239 *orig = NULL; |
| 1531 | |
| 1532 | /* feature requires backend cap */ |
| 1533 | if (!(cpy->cap & PR_CAP_BE)) |
| 1534 | return; |
| 1535 | |
| 1536 | if (def->http_ext == NULL || def->http_ext->fwd == NULL) |
| 1537 | return; |
| 1538 | |
| 1539 | orig = def->http_ext->fwd; |
| 1540 | |
Aurelien DARRAGON | 9ded834 | 2023-01-06 12:16:28 +0100 | [diff] [blame] | 1541 | if (orig->c_mode) |
| 1542 | return; /* copy not supported once compiled */ |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1543 | |
| 1544 | if (!http_ext_7239_prepare(cpy)) |
| 1545 | return; |
| 1546 | |
| 1547 | dest = cpy->http_ext->fwd; |
| 1548 | |
Aurelien DARRAGON | b2bb925 | 2022-12-28 15:37:57 +0100 | [diff] [blame] | 1549 | if (orig->c_file) |
| 1550 | dest->c_file = strdup(orig->c_file); |
| 1551 | dest->c_line = orig->c_line; |
| 1552 | /* proto */ |
| 1553 | dest->p_proto = orig->p_proto; |
| 1554 | /* host */ |
| 1555 | dest->p_host.mode = orig->p_host.mode; |
| 1556 | if (orig->p_host.expr_s) |
| 1557 | dest->p_host.expr_s = strdup(orig->p_host.expr_s); |
| 1558 | /* by - nodename */ |
| 1559 | dest->p_by.nn_mode = orig->p_by.nn_mode; |
| 1560 | if (orig->p_by.nn_expr_s) |
| 1561 | dest->p_by.nn_expr_s = strdup(orig->p_by.nn_expr_s); |
| 1562 | /* by - nodeport */ |
| 1563 | dest->p_by.np_mode = orig->p_by.np_mode; |
| 1564 | if (orig->p_by.np_expr_s) |
| 1565 | dest->p_by.np_expr_s = strdup(orig->p_by.np_expr_s); |
| 1566 | /* for - nodename */ |
| 1567 | dest->p_for.nn_mode = orig->p_for.nn_mode; |
| 1568 | if (orig->p_for.nn_expr_s) |
| 1569 | dest->p_for.nn_expr_s = strdup(orig->p_for.nn_expr_s); |
| 1570 | /* for - nodeport */ |
| 1571 | dest->p_for.np_mode = orig->p_for.np_mode; |
| 1572 | if (orig->p_for.np_expr_s) |
| 1573 | dest->p_for.np_expr_s = strdup(orig->p_for.np_expr_s); |
| 1574 | } |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1575 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1576 | /* duplicate http_ext->xff parameters from <def> to <cpy> |
| 1577 | * performs the required memory allocation and initialization |
| 1578 | */ |
| 1579 | void http_ext_xff_dup(const struct proxy *def, struct proxy *cpy) |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1580 | { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1581 | struct http_ext_xff *dest = NULL; |
| 1582 | struct http_ext_xff *orig = NULL; |
| 1583 | |
| 1584 | if (def->http_ext == NULL || def->http_ext->xff == NULL || |
| 1585 | !http_ext_xff_prepare(cpy)) |
| 1586 | return; |
| 1587 | |
| 1588 | orig = def->http_ext->xff; |
| 1589 | dest = cpy->http_ext->xff; |
| 1590 | |
Aurelien DARRAGON | 730b983 | 2022-12-28 18:53:05 +0100 | [diff] [blame] | 1591 | if (isttest(orig->hdr_name)) |
| 1592 | dest->hdr_name = istdup(orig->hdr_name); |
| 1593 | dest->mode = orig->mode; |
| 1594 | dest->except_net = orig->except_net; |
| 1595 | } |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1596 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1597 | /* duplicate http_ext->xot parameters from <def> to <cpy> |
| 1598 | * performs the required memory allocation and initialization |
| 1599 | */ |
| 1600 | void http_ext_xot_dup(const struct proxy *def, struct proxy *cpy) |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1601 | { |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1602 | struct http_ext_xot *dest = NULL; |
| 1603 | struct http_ext_xot *orig = NULL; |
| 1604 | |
| 1605 | if (def->http_ext == NULL || def->http_ext->xot == NULL || |
| 1606 | !http_ext_xot_prepare(cpy)) |
| 1607 | return; |
| 1608 | |
| 1609 | orig = def->http_ext->xot; |
| 1610 | dest = cpy->http_ext->xot; |
| 1611 | |
Aurelien DARRAGON | f958341 | 2022-12-29 15:45:41 +0100 | [diff] [blame] | 1612 | if (isttest(orig->hdr_name)) |
| 1613 | dest->hdr_name = istdup(orig->hdr_name); |
| 1614 | dest->except_net = orig->except_net; |
| 1615 | } |
Aurelien DARRAGON | 82faad1 | 2022-12-29 18:32:19 +0100 | [diff] [blame] | 1616 | |
Aurelien DARRAGON | b2e2ec5 | 2023-01-09 11:09:03 +0100 | [diff] [blame] | 1617 | /* Allocate new http_ext and initialize it |
| 1618 | * if needed |
| 1619 | * Returns 1 for success and 0 for failure |
| 1620 | */ |
| 1621 | int http_ext_prepare(struct proxy *curproxy) |
| 1622 | { |
| 1623 | if (curproxy->http_ext) |
| 1624 | return 1; /* nothing to do */ |
| 1625 | |
| 1626 | curproxy->http_ext = malloc(sizeof(*curproxy->http_ext)); |
| 1627 | if (!curproxy->http_ext) |
| 1628 | return 0; /* failure */ |
| 1629 | /* first init, set supported ext to NULL */ |
| 1630 | curproxy->http_ext->fwd = NULL; |
| 1631 | curproxy->http_ext->xff = NULL; |
| 1632 | curproxy->http_ext->xot = NULL; |
| 1633 | return 1; |
| 1634 | } |
| 1635 | |
| 1636 | /* duplicate existing http_ext from <defproxy> to <curproxy> |
| 1637 | */ |
| 1638 | void http_ext_dup(const struct proxy *defproxy, struct proxy *curproxy) |
| 1639 | { |
| 1640 | /* copy defproxy.http_ext members */ |
| 1641 | http_ext_7239_dup(defproxy, curproxy); |
| 1642 | http_ext_xff_dup(defproxy, curproxy); |
| 1643 | http_ext_xot_dup(defproxy, curproxy); |
| 1644 | } |
| 1645 | |
| 1646 | /* deep clean http_ext for <curproxy> (if previously allocated) |
| 1647 | */ |
| 1648 | void http_ext_clean(struct proxy *curproxy) |
| 1649 | { |
| 1650 | if (!curproxy->http_ext) |
| 1651 | return; /* nothing to do */ |
| 1652 | /* first, free supported ext */ |
| 1653 | http_ext_7239_clean(curproxy); |
| 1654 | http_ext_xff_clean(curproxy); |
| 1655 | http_ext_xot_clean(curproxy); |
| 1656 | |
| 1657 | /* then, free http_ext */ |
| 1658 | ha_free(&curproxy->http_ext); |
| 1659 | } |
| 1660 | |
| 1661 | /* soft clean (only clean http_ext if no more options are used) */ |
| 1662 | void http_ext_softclean(struct proxy *curproxy) |
| 1663 | { |
| 1664 | if (!curproxy->http_ext) |
| 1665 | return; /* nothing to do */ |
| 1666 | if (!curproxy->http_ext->fwd && |
| 1667 | !curproxy->http_ext->xff && |
| 1668 | !curproxy->http_ext->xot) { |
| 1669 | /* no more use for http_ext, all options are disabled */ |
| 1670 | http_ext_clean(curproxy); |
| 1671 | } |
| 1672 | } |
| 1673 | |
Aurelien DARRAGON | c07001b | 2023-01-24 17:55:09 +0100 | [diff] [blame] | 1674 | /* Perform some consitency checks on px.http_ext after parsing |
| 1675 | * is completed. |
| 1676 | * We make sure to perform a softclean in case some options were |
| 1677 | * to be disabled in this check. This way we can release some memory. |
| 1678 | * Returns a composition of ERR_NONE, ERR_ALERT, ERR_FATAL, ERR_WARN |
| 1679 | */ |
| 1680 | static int check_http_ext_postconf(struct proxy *px) { |
| 1681 | int err = ERR_NONE; |
| 1682 | |
| 1683 | if (px->http_ext) { |
| 1684 | /* consistency check for http_ext */ |
| 1685 | if (px->mode != PR_MODE_HTTP && !(px->options & PR_O_HTTP_UPG)) { |
| 1686 | /* http is disabled on px, yet it is required by http_ext */ |
| 1687 | if (px->http_ext->fwd) { |
| 1688 | ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", |
| 1689 | "forwarded", proxy_type_str(px), px->id); |
| 1690 | err |= ERR_WARN; |
| 1691 | http_ext_7239_clean(px); |
| 1692 | } |
| 1693 | if (px->http_ext->xff) { |
| 1694 | ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", |
| 1695 | "forwardfor", proxy_type_str(px), px->id); |
| 1696 | err |= ERR_WARN; |
| 1697 | http_ext_xff_clean(px); |
| 1698 | } |
| 1699 | if (px->http_ext->xot) { |
| 1700 | ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", |
| 1701 | "originalto", proxy_type_str(px), px->id); |
| 1702 | err |= ERR_WARN; |
| 1703 | http_ext_xot_clean(px); |
| 1704 | } |
| 1705 | } else if (px->http_ext->fwd) { |
| 1706 | /* option "forwarded" may need to compile its expressions */ |
| 1707 | err |= proxy_http_compile_7239(px); |
| 1708 | } |
| 1709 | /* http_ext post init early cleanup */ |
| 1710 | http_ext_softclean(px); |
| 1711 | |
| 1712 | } |
| 1713 | return err; |
| 1714 | } |
| 1715 | |
| 1716 | REGISTER_POST_PROXY_CHECK(check_http_ext_postconf); |
Aurelien DARRAGON | 82faad1 | 2022-12-29 18:32:19 +0100 | [diff] [blame] | 1717 | /* |
| 1718 | * =========== CONV =========== |
| 1719 | * related converters |
| 1720 | */ |
| 1721 | |
Aurelien DARRAGON | 5c6f86f | 2022-12-30 16:23:08 +0100 | [diff] [blame] | 1722 | /* input: string representing 7239 forwarded header single value |
| 1723 | * does not take arguments |
| 1724 | * output: 1 if header is RFC compliant, 0 otherwise |
| 1725 | */ |
| 1726 | static int sample_conv_7239_valid(const struct arg *args, struct sample *smp, void *private) |
| 1727 | { |
| 1728 | struct ist input = ist2(smp->data.u.str.area, smp->data.u.str.data); |
| 1729 | |
| 1730 | smp->data.type = SMP_T_BOOL; |
| 1731 | smp->data.u.sint = !!http_validate_7239_header(input, FORWARDED_HEADER_ALL, NULL); |
| 1732 | return 1; |
| 1733 | } |
| 1734 | |
Aurelien DARRAGON | 6fb58b8 | 2022-12-30 16:37:03 +0100 | [diff] [blame] | 1735 | /* input: string representing 7239 forwarded header single value |
| 1736 | * argument: parameter name to look for in the header |
| 1737 | * output: header parameter raw value, as a string |
| 1738 | */ |
| 1739 | static int sample_conv_7239_field(const struct arg *args, struct sample *smp, void *private) |
| 1740 | { |
| 1741 | struct ist input = ist2(smp->data.u.str.area, smp->data.u.str.data); |
| 1742 | struct buffer *output; |
| 1743 | struct forwarded_header_ctx ctx; |
| 1744 | int validate; |
| 1745 | int field = 0; |
| 1746 | |
| 1747 | if (strcmp(args->data.str.area, "proto") == 0) |
| 1748 | field = FORWARDED_HEADER_PROTO; |
| 1749 | else if (strcmp(args->data.str.area, "host") == 0) |
| 1750 | field = FORWARDED_HEADER_HOST; |
| 1751 | else if (strcmp(args->data.str.area, "for") == 0) |
| 1752 | field = FORWARDED_HEADER_FOR; |
| 1753 | else if (strcmp(args->data.str.area, "by") == 0) |
| 1754 | field = FORWARDED_HEADER_BY; |
| 1755 | |
| 1756 | validate = http_validate_7239_header(input, FORWARDED_HEADER_ALL, &ctx); |
| 1757 | if (!(validate & field)) |
| 1758 | return 0; /* invalid header or header does not contain field */ |
| 1759 | output = get_trash_chunk(); |
| 1760 | switch (field) { |
| 1761 | case FORWARDED_HEADER_PROTO: |
| 1762 | if (ctx.proto == FORWARDED_HEADER_HTTP) |
| 1763 | chunk_appendf(output, "http"); |
| 1764 | else if (ctx.proto == FORWARDED_HEADER_HTTPS) |
| 1765 | chunk_appendf(output, "https"); |
| 1766 | break; |
| 1767 | case FORWARDED_HEADER_HOST: |
| 1768 | chunk_istcat(output, ctx.host); |
| 1769 | break; |
| 1770 | case FORWARDED_HEADER_FOR: |
| 1771 | chunk_istcat(output, ctx.nfor.raw); |
| 1772 | break; |
| 1773 | case FORWARDED_HEADER_BY: |
| 1774 | chunk_istcat(output, ctx.nby.raw); |
| 1775 | break; |
| 1776 | default: |
| 1777 | break; |
| 1778 | } |
| 1779 | smp->flags &= ~SMP_F_CONST; |
| 1780 | smp->data.type = SMP_T_STR; |
| 1781 | smp->data.u.str = *output; |
| 1782 | return 1; |
| 1783 | } |
| 1784 | |
Aurelien DARRAGON | 07d6753 | 2022-12-30 16:45:42 +0100 | [diff] [blame] | 1785 | /* input: substring representing 7239 forwarded header node |
| 1786 | * output: forwarded header nodename translated to either |
| 1787 | * ipv4 address, ipv6 address or str |
| 1788 | * ('_' prefix if obfuscated, or "unknown" if unknown) |
| 1789 | */ |
| 1790 | static int sample_conv_7239_n2nn(const struct arg *args, struct sample *smp, void *private) |
| 1791 | { |
| 1792 | struct ist input = ist2(smp->data.u.str.area, smp->data.u.str.data); |
| 1793 | struct forwarded_header_node ctx; |
| 1794 | struct buffer *output; |
| 1795 | |
| 1796 | if (http_7239_extract_node(&input, &ctx, 1) == 0) |
| 1797 | return 0; /* could not extract node */ |
| 1798 | switch (ctx.nodename.type) { |
| 1799 | case FORWARDED_HEADER_UNK: |
| 1800 | output = get_trash_chunk(); |
| 1801 | chunk_appendf(output, "unknown"); |
| 1802 | smp->flags &= ~SMP_F_CONST; |
| 1803 | smp->data.type = SMP_T_STR; |
| 1804 | smp->data.u.str = *output; |
| 1805 | break; |
| 1806 | case FORWARDED_HEADER_OBFS: |
| 1807 | output = get_trash_chunk(); |
| 1808 | chunk_appendf(output, "_"); /* append obfs prefix */ |
| 1809 | chunk_istcat(output, ctx.nodename.obfs); |
| 1810 | smp->flags &= ~SMP_F_CONST; |
| 1811 | smp->data.type = SMP_T_STR; |
| 1812 | smp->data.u.str = *output; |
| 1813 | break; |
| 1814 | case FORWARDED_HEADER_IP: |
| 1815 | if (ctx.nodename.ip.ss_family == AF_INET) { |
| 1816 | smp->data.type = SMP_T_IPV4; |
| 1817 | smp->data.u.ipv4 = ((struct sockaddr_in *)&ctx.nodename.ip)->sin_addr; |
| 1818 | } |
| 1819 | else if (ctx.nodename.ip.ss_family == AF_INET6) { |
| 1820 | smp->data.type = SMP_T_IPV6; |
| 1821 | smp->data.u.ipv6 = ((struct sockaddr_in6 *)&ctx.nodename.ip)->sin6_addr; |
| 1822 | } |
| 1823 | else |
| 1824 | return 0; /* unsupported */ |
| 1825 | break; |
| 1826 | default: |
| 1827 | return 0; /* unsupported */ |
| 1828 | } |
| 1829 | return 1; |
| 1830 | } |
| 1831 | |
Aurelien DARRAGON | 9a273b4 | 2022-12-30 16:56:08 +0100 | [diff] [blame] | 1832 | /* input: substring representing 7239 forwarded header node |
| 1833 | * output: forwarded header nodeport translated to either |
| 1834 | * integer or str for obfuscated ('_' prefix) |
| 1835 | */ |
| 1836 | static int sample_conv_7239_n2np(const struct arg *args, struct sample *smp, void *private) |
| 1837 | { |
| 1838 | struct ist input = ist2(smp->data.u.str.area, smp->data.u.str.data); |
| 1839 | struct forwarded_header_node ctx; |
| 1840 | struct buffer *output; |
| 1841 | |
| 1842 | if (http_7239_extract_node(&input, &ctx, 1) == 0) |
| 1843 | return 0; /* could not extract node */ |
| 1844 | |
| 1845 | switch (ctx.nodeport.type) { |
| 1846 | case FORWARDED_HEADER_UNK: |
| 1847 | return 0; /* not provided */ |
| 1848 | case FORWARDED_HEADER_OBFS: |
| 1849 | output = get_trash_chunk(); |
| 1850 | chunk_appendf(output, "_"); /* append obfs prefix */ |
| 1851 | chunk_istcat(output, ctx.nodeport.obfs); |
| 1852 | smp->flags &= ~SMP_F_CONST; |
| 1853 | smp->data.type = SMP_T_STR; |
| 1854 | smp->data.u.str = *output; |
| 1855 | break; |
| 1856 | case FORWARDED_HEADER_PORT: |
| 1857 | smp->data.type = SMP_T_SINT; |
| 1858 | smp->data.u.sint = ctx.nodeport.port; |
| 1859 | break; |
| 1860 | default: |
| 1861 | return 0; /* unsupported */ |
| 1862 | } |
| 1863 | |
| 1864 | return 1; |
| 1865 | } |
| 1866 | |
Aurelien DARRAGON | 82faad1 | 2022-12-29 18:32:19 +0100 | [diff] [blame] | 1867 | /* Note: must not be declared <const> as its list will be overwritten */ |
| 1868 | static struct sample_conv_kw_list sample_conv_kws = {ILH, { |
Aurelien DARRAGON | 5c6f86f | 2022-12-30 16:23:08 +0100 | [diff] [blame] | 1869 | { "rfc7239_is_valid", sample_conv_7239_valid, 0, NULL, SMP_T_STR, SMP_T_BOOL}, |
Aurelien DARRAGON | 6fb58b8 | 2022-12-30 16:37:03 +0100 | [diff] [blame] | 1870 | { "rfc7239_field", sample_conv_7239_field, ARG1(1,STR), NULL, SMP_T_STR, SMP_T_STR}, |
Aurelien DARRAGON | 07d6753 | 2022-12-30 16:45:42 +0100 | [diff] [blame] | 1871 | { "rfc7239_n2nn", sample_conv_7239_n2nn, 0, NULL, SMP_T_STR, SMP_T_ANY}, |
Aurelien DARRAGON | 9a273b4 | 2022-12-30 16:56:08 +0100 | [diff] [blame] | 1872 | { "rfc7239_n2np", sample_conv_7239_n2np, 0, NULL, SMP_T_STR, SMP_T_ANY}, |
Aurelien DARRAGON | 82faad1 | 2022-12-29 18:32:19 +0100 | [diff] [blame] | 1873 | { NULL, NULL, 0, 0, 0 }, |
| 1874 | }}; |
| 1875 | |
| 1876 | INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws); |