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