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